irpg Community
เขียนปลั๊คอินเปลี่ยนเมนูหลักอยู่ส่วนไหนครับ - Printable Version

+- irpg Community (https://irpg.in.th)
+-- Forum: irpg Fabella RPG Maker Academia (https://irpg.in.th/forum-4.html)
+--- Forum: RPG Maker Engine School (https://irpg.in.th/forum-7.html)
+--- Thread: เขียนปลั๊คอินเปลี่ยนเมนูหลักอยู่ส่วนไหนครับ (/thread-2888.html)



เขียนปลั๊คอินเปลี่ยนเมนูหลักอยู่ส่วนไหนครับ - bezblue - 06-04-2016

พอดีอยากทำเมนูเกมขึ้นมาเองครับ คอคำคีเวิดยังดีครับ หมดปัญญาจริงๆ


RE: เขียนปลั๊คอินเปลี่ยนเมนูหลักอยู่ส่วนไหนครับ - XthemeCore - 06-04-2016

ก่อนอื่นควรศึกษาการเขียนโปรแกรมเชิงวัตถุก่อนครับ http://www.no-poor.com/JavaandAi/chapter1-java.htm
(อันนี้คือแผนผังแบบสรุป https://www.mindmeister.com/198142531/object-oriented-programming-oop)
**หมายเหตุ: Java กับ JavaScript เป็นคนละภาษากันแค่ต้องการให้ทราบหลักการการเขียนโปรแกรมเชิงวัตถุเท่านั้น

จากนั้นก็ลงมือครับ

เมนูจะประกอบด้วยตัวเมนู กับ คำสั่งจะที่จะทำเมื่อผู้เล่นกดเมนูนั้นๆครับ

ตัวเมนูนั้นสร้างมาจาก class Window_Command ก็ได้ครับ (ดูไฟล์ rpg_windows.js)
ซึ่งเราจะพบว่า Window_Command สืบทอดมาจาก Window_Selectable อีกที และคลาสที่เป็นต้นสายจริงๆคือ Window_Base

ทีนี้ถ้าเราอยากลัดขั้นตอน ให้ลองหา class ที่สืบทอดจาก Windows_Command มาแก้ดูครับ
ตัวอย่างที่น่าจะใกล้เคียงกับที่ท่าน bezblue อยากทำมากที่สุดน่าจะเป็น Window_MenuCommand

[Image: urd-anim-cur.jpg?w=620]

Window_MenuCommand ตัวนี้ก็คือเมนูตอนที่เรากด x ในเกมนั่นแหละครับ (เมนูหลัก) จากภาพคือเมนูที่อยู่ในกรอบด้านซ้าย มาดูโครงสร้างซักนิด

ส่วนต้นแบบคลาส

Code:
function Window_MenuCommand() {
    this.initialize.apply(this, arguments);
}

Window_MenuCommand.prototype = Object.create(Window_Command.prototype);
Window_MenuCommand.prototype.constructor = Window_MenuCommand;

Window_MenuCommand.prototype.initialize = function(x, y) {
    Window_Command.prototype.initialize.call(this, x, y);
    this.selectLast();
};

โค้ดด้านบนนี้จะเป็นคุณสมบัติการสืบทอดของคลาส Window_MenuCommand ว่าสืบทอดมาจากคลาส Window_Command โดยสังเกตได้ ซึ่งก็คือโค้ด Window_MenuCommand.prototype = Object.create(Window_Command.prototype); ถ้าสังเกตดีๆจะเห็นว่าในโค้ดมีคำว่า prototype เต็มไปหมด ถ้าจะอธิบายง่ายๆให้เรานึกภาพว่ามีโรงงานแห่งหนึ่งเป็นโรงงานผลิตคลาส ซึ่งแน่นอนว่าเขาจะต้องมีต้นแบบหรือแม่แบบ (prototype) จริงไหมครับ เพื่อให้ผลิตออกมาได้วัตถุที่มีคุณสมบัติเหมือนกันทุกประการ อันนี้ก็เหมือนกันครับ ตรงโค้ดส่วนนี้ยังไม่ใช่การสร้าง Window_MenuCommand เลย แต่เป็นการเขียนต้นแบบของ Window_MenuCommand ขึ้นมาซึ่งเราจะเอาไปสรเางเป็นวัตถุภายหลัง

ทีนี้วัตถุที่เราจะสร้างมันก็ควรจะถูกกำหนดค่าเริ่มต้นใช่ไหมครับ ในขณะที่ถูกสร้าง ซึ่งส่วนนี้จะอยู่ในฟังก์ชัน initialize นั่นเอง

แนะนำว่าท่าน bezblue ควรศึกษาการเขียนโปรแกรมเชิงวัตถุก่อนนะครับจะได้เข้าใจที่อธิบายเพราะมันอธิบายยากจริง ถ้าเข้าใจหลักการเขียนโปรแกรมเชิงวัตถุก็จะเข้าใจโค้ดได้ง่ายๆเลย เหมือนอย่างโค้ด Window_Command.prototype.initialize.call(this, x, y); ซึ่งอยู่ในฟังก์ชัน initialize ของคลาส Window_MenuCommand นั้น เป็นการเรียกใช้ฟังก์ชัน initialize ของคลาสแม่(Window_Command) เป็นต้น


เอาเป็นว่าอธิบายต่อน่าจะยาว เดี๋ยวผมจะใบ้ส่วนสำคัญให้ลองโมเพิ่มนะครับ ลองดูโค้ดของคลาส Window_MenuCommand ไปเรื่อยจะเป็นเห็นว่าประกอบด้วยฟังก์ชันมากมายลองมาดูฟังก์ชันนี้กันครับ

Code:
Window_MenuCommand.prototype.addMainCommands = function() {
    var enabled = this.areMainCommandsEnabled();
    if (this.needsCommand('item')) {
        this.addCommand(TextManager.item, 'item', enabled);
    }
    if (this.needsCommand('skill')) {
        this.addCommand(TextManager.skill, 'skill', enabled);
    }
    if (this.needsCommand('equip')) {
        this.addCommand(TextManager.equip, 'equip', enabled);
    }
    if (this.needsCommand('status')) {
        this.addCommand(TextManager.status, 'status', enabled);
    }
};
ส่วนนี้เป็นส่วนเพิ่มรายการเมนูนั่นเอง TextManager.item คือชื่อเมนูไอเทมที่จะแสดงให้ผู้เล่นเห็นนั่นเอง ซึ่งผู็พัฒนา RMMV ได้เขียนโค้ดให้มีความเป็นระเบียบอย่างมาก และเพื่อให้สามารถใช้ร่วมกับ Editor ได้ เลยให้มันเรียกจาก TextManager ซึ่งเป็นคลาสๆหนึ่งนั่นเอง กรณีทำเมนูเองเราาสามารถเขียนเป็น this.addCommand('เมนูของฉัน', 'mymenu', enabled); ได้ ส่วนตรงที่เป็น 'mymenu' หรือ 'item' (ถ้าดูจากโค้ดด้านบน) เป็นส่วนที่ผมกำลังจะอธิบายต่อจากนี้ครับ

ส่วนสร้างวัตถุจากต้นแบบ

มาถึงขั้นตอนการสร้างวัตถุจริงๆแล้ว ให้ดูที่ไฟล์ rpg_scene.js ครับ โดยมาดูที่ฟังก์ชัน createCommandWindow ของ Scene_Menu
Code:
Scene_Menu.prototype.createCommandWindow = function() {
    this._commandWindow = new Window_MenuCommand(0, 0);
    this._commandWindow.setHandler('item',      this.commandItem.bind(this));
    this._commandWindow.setHandler('skill',     this.commandPersonal.bind(this));
    this._commandWindow.setHandler('equip',     this.commandPersonal.bind(this));
    this._commandWindow.setHandler('status',    this.commandPersonal.bind(this));
    this._commandWindow.setHandler('formation', this.commandFormation.bind(this));
    this._commandWindow.setHandler('options',   this.commandOptions.bind(this));
    this._commandWindow.setHandler('save',      this.commandSave.bind(this));
    this._commandWindow.setHandler('gameEnd',   this.commandGameEnd.bind(this));
    this._commandWindow.setHandler('cancel',    this.popScene.bind(this));
    this.addWindow(this._commandWindow);
};

จากโค้ด this._commandWindow = new Window_MenuCommand(0, 0); ก็คือการสร้างวัตถุ Window_MenuCommand นั่นเอง อธิบายโค้ดง่ายๆก็เราไปสั่ง new (สร้างใหม่) มันนั่นแหละครับ โดยจะเห็นว่ามีการใส่ (0, 0) ด้วย นั่นคือการรับพารามิเตอร์ (ถ้าไม่ทราบว่าคืออะไรควรศึกษาการเขียนฟังก์ชันภาษา C เบื้องต้น) จากฟังก์ชัน initialize ที่ผมได้อธิบายไว้ตอนต้นนั่นเอง

ทีนี้คำถามอาจจะเป็น this._ คืออะไร ก็คือ Scene_Menu จัดเป็นวัตถุๆหนึ่งเหมือนกัน และในวัตถุนั้น สามารถมีวัตถุย่อยๆอยู่ภายในตัวมันได้ ก็เหมือนคนเราที่มี ปอด หัวใจ ตับ เป็นส่วนประกอบภายในล่ะครับ ทีนี้การใช้คำว่า this คือการเรียกตัวเองครับ เช่นถ้าเราต้องการเรียกฟงัก์ชั่น move() ที่อยู่ในคลาสนั้นเองก็ใช้ this.move() ส่วน _ เป็นการเรียกใช้ตัวแปร หรือฟังก์ชันที่มีการประกาศเป็น private (ถ้าไม่ทราบว่า private คืออะไรกลับไปศึกษาด้านบน) ซึ่งตัวแปรหรือฟงัก์ชันประเภทนี้จะถูกเรียกจากคลาสอื่นไม่ได้

ต่อมา มาดูที่ this._commandWindow.setHandler('item', this.commandItem.bind(this)); กันครับ ตรงนี้ล่ะที่จะต่อจากเมื่อกี้ ที่ผมได้กล่าวว่าจะอธิบายว่า 'mynemu' หรือ 'item' เอามาใช้ตรงไหน ก็เรามาใช้ตรงนี้ไงครับ แมวเบิกบาน โค้ดนี้เป็นการ bind คำสั่ง อธิบายง่ายๆคือ เมื่อผู้เล่นกดเลือกเมนูไอเทมแล้วจะไปไหนต่อ โดยชื่อจะต้องตรงกับตอนที่เรา addCommand ด้วยนะครับ (กลับไปดูด้านบน) ส่วนสิ่งที่เราต้องการใช้ทำก็คือ this.commandItem.bind(this) นั่นเอง จริงๆตรงนี้เราใส่ฟังก์ชันอะไรลงไปก็ได้ครับเช่นถ้าผมจะใช้ฟังก์ชัน move() ซึ่งอยู่ในคลาสเดียวกัน ก็จะเขียนเป็น this._commandWindow.setHandler('mymenu', this.move());

แต่ถ้าถามว่า this.commandItem.bind(this) มันจะไปเรียกฟังก์ชันไหน ก็ตอบว่าฟังก์ชันนี้ครับ
Code:
Scene_Menu.prototype.commandItem = function() {
    SceneManager.push(Scene_Item);
};

จริงๆมีอะไรอธิบายอีกเยอะเลย แต่เอาแบบเร็วๆไปก่อนละกันครับ เอ้าดื่ม
มีอะไรสงสัยถามในนี้ได้เลย


RE: เขียนปลั๊คอินเปลี่ยนเมนูหลักอยู่ส่วนไหนครับ - XthemeCore - 06-04-2016

ด้านบนที่อธิบายไปยังไม่ใช่การเขียนปลั๊กอินเลยนะครับ การเขียนปลั๊กอินก็มีรูปแบบการเขียนของมันอีก
เพราะปลั๊กอินจะต้องทำงานร่วมกับ Plugin Manager ในตัว Editor ของ RMMV ด้วย ถ้าเราจะเขียนปลั๊กอินแบบง่ายๆไม่รับพารามิเตอร์อะไรเลย เป็นแค่การเพิ่มเมนูเข้าไปเฉยๆ ก็จะออกมาประมาณนี้ (สร้างไฟล์ .js ใหม่แล้ว save ไวในโฟลเดอร์ plugins)
Code:
//=============================================================================
// ชื่อไฟล์.js
//=============================================================================

/*:
* @plugindesc คำอธิบายปลั๊กอิน
* @author ชื่อคนสร้าง
*
* @help คำอธิบายในหน้าต่าง Help
*/

(function() {
    //โค้ดทั้งหมดอยู่ในนี้
})();



RE: เขียนปลั๊คอินเปลี่ยนเมนูหลักอยู่ส่วนไหนครับ - bezblue - 06-04-2016

เจอแล้วครับ อยู่พวก Windows_Command นี่เอง ขอบคุณมากๆครับ T^T
ท่าน XthemeCore อธิบายละเอียดเข้าใจง่ายครับ แสดงว่าทำออนไลน์ node js นี่ได้สบายๆเลยนะครับนิ ฮ่าๆ