การออกแบบแอปพลิเคชัน Javascript MVC (แคนวาส)


9

ฉันมีปัญหาในการเข้าใจวิธีการจัดโครงสร้าง / ออกแบบแอปพลิเคชั่นแคนวาสโดยใช้ MVC เหมือนกับแนวทางใน Javascript UI จะค่อนข้างลื่นไหลและมีชีวิตชีวาเกมค่อนข้างง่าย แต่เน้นหนักไปที่การทวีตและภาพเคลื่อนไหว ฉันเข้าใจวิธีการทำงานของ MVC ในหลักการ แต่ไม่ใช่ในทางปฏิบัติ ฉันเริ่มดื่มเหล้าจากที่นี่อ่านมาก ๆ และตอนนี้ฉันก็สับสนเหมือนตอนที่ฉันเริ่ม

รายละเอียดบางอย่างเกี่ยวกับพื้นที่แอพพลิเคชัน:

  • เฟรมเวิร์กเกมหลายหน้าจอ - เกมหลายเกมจะอยู่ภายในกรอบ "หน้าจอ" UI ทั่วไปนี้รวมถึง: การตั้งค่าข้อมูลเลือกความยากลำบากเมนูหลักเป็นต้น
  • วิธีการป้อนข้อมูลหลายวิธี
  • องค์ประกอบ UI ทั่วไปเช่นแถบเมนูด้านบนในบางหน้าจอ
  • ความเป็นไปได้ของการใช้วิธีการแสดงผลที่แตกต่างกัน (canvas / DOM / webGL)

ในตอนนี้ฉันมี AppModel, AppController และ AppView จากที่นี่ฉันวางแผนที่จะเพิ่ม "หน้าจอ" แต่ละรายการและแนบไปยัง AppView แต่สิ่งที่เกี่ยวกับสิ่งต่าง ๆ เช่นแถบเมนูด้านบนพวกเขาควรเป็น MVC สามกลุ่มอื่นหรือไม่ ฉันจะติดตั้งที่ไหนและอย่างไรโดยไม่ต้องมีการเชื่อมต่ออย่างแน่นหนา?

เป็นวิธีปฏิบัติที่ยอมรับได้หรือไม่ที่จะมี MVC สามกลุ่มในอีกกลุ่มหนึ่ง? ฉันสามารถเพิ่ม "หน้าจอ" แต่ละรายการลงใน AppView ได้หรือไม่ "triad" เป็นคำที่ยอมรับได้หรือไม่!

ใจของฉันกำลังละลายภายใต้ตัวเลือก ... ฉันรู้สึกว่าฉันขาดอะไรบางอย่างพื้นฐานที่นี่ ฉันมีวิธีแก้ปัญหาแล้วและทำงานโดยไม่ใช้วิธี MVC แต่ได้จบลงด้วยซุปคู่ที่แน่นหนา - ตรรกะและมุมมองและรวมกันในปัจจุบัน แนวคิดคือการเปิดขึ้นและอนุญาตให้เปลี่ยนมุมมองได้ง่ายขึ้น (เช่นการสลับมุมมองแคนวาสกับมุมมองแบบ DOM)

ไลบรารีปัจจุบันที่ใช้: require.js, createJS, ขีดล่าง, GSAP, การใช้ MVC แบบรีดด้วยมือ

พอยน์เตอร์ตัวอย่างและอื่น ๆ โดยเฉพาะอย่างยิ่งเกี่ยวกับการออกแบบที่แท้จริงของสิ่งของและแยก "หน้าจอ" เป็น M, V หรือ C ที่เหมาะสม

... หรือวิธีการอื่นที่เหมาะสมกว่า MVC

[NB ถ้าคุณเคยเห็นคำถามนี้มาก่อนเพราะฉันถามในชุมชน stackexchange ที่ไม่ถูกต้อง 2 แห่ง ... สมองของฉันหยุดทำงาน]


1
ดูเหมือนว่าคุณจะพบไซต์ที่เหมาะสมที่สุด Gamedev ไม่ต้องการคำถามของคุณ?
Robert Harvey

@ RobertHarvey คิดว่ามันอาจจะเกี่ยวข้องมากขึ้นที่นี่ ... อย่างน้อยฉันก็หวังเช่นนั้น!
wigglyworm

คำตอบ:


3

MVC ได้รับการกล่าวถึงในหลายสถานที่ดังนั้นจึงไม่ควรที่จะทำซ้ำอีกครั้งที่นี่ โดยพื้นฐานแล้วคุณต้องการให้กราฟวัตถุผู้ช่วยเหลือและตรรกะอยู่ในระดับชั้นของแบบจำลอง มุมมองจะเป็นหน้าจอที่ผลักออกมาเพื่อเติมส่วนแบบไดนามิกของหน้า (และอาจมีจำนวนตรรกะและผู้ช่วยเหลือจำนวนเล็กน้อย) และคอนโทรลเลอร์ซึ่งเป็นการใช้งานที่มีน้ำหนักเบาเพื่อให้บริการหน้าจอตามสิ่งที่มีให้จากกราฟวัตถุผู้ช่วยและตรรกะ

แบบ

นี่ควรเป็นที่ที่เนื้อของแอปพลิเคชันอยู่ มันสามารถทำเป็นชั้นออกเป็นชั้นบริการ, ชั้นตรรกะและชั้นนิติบุคคล สิ่งนี้หมายความว่าอย่างไรสำหรับตัวอย่างของคุณ?

เอนทิตีเลเยอร์

นี่ควรเป็นคำจำกัดความของรูปแบบเกมและพฤติกรรมภายในของคุณ ตัวอย่างเช่นหากคุณมีเกมสำหรับเรือกวาดทุ่นระเบิดนี่จะเป็นคำจำกัดความของกระดานและสี่เหลี่ยมพร้อมกับวิธีที่พวกเขาเปลี่ยนสถานะภายในของพวกเขา

function Location(x,y){
 this.x = x;
 this.y = y;
}
function MineTile(x,y){
 this.flagged = false;
 this.hasMine = false;
 this.pristine = true;
 this.location = new Location(x,y);
}
MineTile.prototype.expose = function(){
 if( this.hasMine ) return false;
 this.pristine = false;
 return this.location;
};

ดังนั้น MineTile จะรู้สถานะภายในของมันเช่นถ้ามันกำลังแสดงหรือถูกตรวจสอบ ( this.pristine) ถ้ามันเป็นหนึ่งในแผ่นกระเบื้องที่มีเหมือง ( this.hasMine) แต่จะไม่ตัดสินว่าควรจะมีเหมืองหรือไม่ นั่นจะขึ้นอยู่กับชั้นตรรกะ (ในการก้าวต่อไปสู่ ​​OOP ยิ่งขึ้น MineTile สามารถสืบทอดจากไทล์ทั่วไปได้)

ชั้นตรรกะ

นี่ควรเป็นวิธีที่ซับซ้อนที่แอปพลิเคชันจะโต้ตอบกับโหมดการเปลี่ยนแปลงการรักษาสถานะ ฯลฯ ดังนั้นนี่จะเป็นที่ซึ่งรูปแบบของผู้ไกล่เกลี่ยจะถูกนำมาใช้เพื่อรักษาสถานะของเกมปัจจุบัน นี่จะเป็นที่ที่ตรรกะของเกมอาศัยอยู่เพื่อกำหนดสิ่งที่เกิดขึ้นในระหว่างการเล่นเกมหรือสำหรับการตั้งค่าว่า MineTiles ใดจะมีระเบิด มันจะทำการเรียกเข้าไปยังเลเยอร์เอ็นติตี้เพื่อรับระดับอินสแตนซ์ตามพารามิเตอร์ที่กำหนดตามหลักเหตุผล

var MineSweeperLogic = {
 construct: function(x,y,difficulty){
  var mineSet = [];
  var bombs = 7;
  if( difficulty === "expert" ) bombs = 15;
  for( var i = 0; i < x; i++ ){
   for( var j = 0; i j < y; j++ ){
    var mineTile = new MineTile(i,j);
    mineTile.hasMine = bombs-- > 0;
    mineSet.push(mineTile);
   }
  }
  return mineSet;
 },
 mineAt: function(x,y,mineSet){
  for( var i = 0; i < mineSet.length; i++ )
   if( mineSet[i].x === x && mineSet[i].y === y ) return mineSet[i];
 }
};

ชั้นบริการ

นี่จะเป็นที่ซึ่งคอนโทรลเลอร์สามารถเข้าถึงได้ มันจะสามารถเข้าถึงชั้นตรรกะสำหรับการสร้างเกม อาจมีการโทรระดับสูงในเลเยอร์บริการเพื่อดึงเกมที่มีการอินสแตนซ์เต็มหรือสถานะเกมที่แก้ไข

function MineSweeper(x,y,difficulty){
 this.x = x;
 thix.y = y;
 this.difficulty = difficulty;
 this.mineSet = MineSweeperLogic.construct(x,y,difficulty);
}
MineSweeper.prototype.expose = function(x,y){
 return MineSweeperLogic.mineAt(x,y,this.mineSet).expose();
}

ตัวควบคุม

ตัวควบคุมควรมีน้ำหนักเบาเป็นหลักนี่คือสิ่งที่ถูกเปิดเผยในฐานะที่เป็นลูกค้าของโมเดล จะมีคอนโทรลเลอร์หลายตัวดังนั้นการจัดโครงสร้างจะกลายเป็นสิ่งสำคัญ การเรียกฟังก์ชั่นของคอนโทรลเลอร์จะเป็นสิ่งที่การโทรจาวาสคริปต์เข้าชมจะขึ้นอยู่กับกิจกรรม UI สิ่งเหล่านี้ควรแสดงพฤติกรรมที่มีอยู่ในชั้นบริการแล้วเติมหรือในกรณีนี้แก้ไขมุมมองสำหรับลูกค้า

function MineSweeperController(ctx){
 var this.context = ctx;
}
MineSweeperController.prototype.Start = function(x,y,difficulty){
 this.game = new MineSweeper(x,y,difficulty);
 this.view = new MineSweeperGameView(this.context,this.game.x,this.game.y,this.game.mineSet);
 this.view.Update();
};
MineSweeperController.prototype.Select = function(x,y){
 var result = this.game.expose(x,y);
 if( result === false ) this.GameOver();
 this.view.Select(result);
};
MineSweeperController.prototype.GameOver = function(){
 this.view.Summary(this.game.FinalScore());
};

ดู

มุมมองควรถูกจัดระเบียบสัมพันธ์กับพฤติกรรมของตัวควบคุม พวกเขาอาจจะเป็นส่วนที่เข้มข้นที่สุดในใบสมัครของคุณเนื่องจากมันเกี่ยวข้องกับการวาดภาพ

function MineSweeperGameView(ctx,x,y,mineSet){
 this.x = x;
 this.y = y;
 this.mineSet = mineSet;
 this.context = ctx;
}
MineSweeperGameView.prototype.Update = function(){
 //todo: heavy canvas modification
 for(var mine in this.mineSet){}
 this.context.fill();
}

ดังนั้นตอนนี้คุณมีการตั้งค่า MVC ทั้งหมดสำหรับเกมนี้ หรืออย่างน้อยตัวอย่างกระดูกเปลือยการเขียนเกมออกมาก็คงจะมากเกินไป

เมื่อดำเนินการเสร็จสิ้นจะต้องมีขอบเขตทั่วโลกสำหรับแอปพลิเคชันที่ใดที่หนึ่ง สิ่งนี้จะเก็บอายุการใช้งานของคอนโทรลเลอร์ปัจจุบันของคุณซึ่งเป็นเกตเวย์ของสแต็ก MVC ทั้งหมดในสถานการณ์นี้

var currentGame;
var context = document.getElementById("masterCanvas").getContext('2d');
startMineSweeper.click = function(){
 currentGame = new MineSweeperController(context);
 currentGame.Start(25,25,"expert");
};

การใช้รูปแบบ MVC นั้นทรงพลังมาก แต่ไม่ต้องกังวลมากเกินไปเกี่ยวกับการยึดติดกับทุกความแตกต่างของพวกเขา ในที่สุดมันเป็นประสบการณ์เกมที่จะตัดสินว่าแอปพลิเคชันนั้นประสบความสำเร็จหรือไม่ :)

เพื่อการพิจารณา: อย่าปล่อยให้นักบินอวกาศสถาปัตย์ตกใจกับ Joel Spolsky


ขอบคุณ @ TravisJ - ฉัน upvoted เป็นคำอธิบายที่ดีของ MVC ที่เกี่ยวข้องกับเกม ยังไม่ชัดเจนในบางประเด็นฉันคิดว่าอย่างที่คุณพูดฉันกำลังจมอยู่กับความแตกต่างของลวดลายและมันทำให้ฉันก้าวไปข้างหน้า สิ่งหนึ่งที่ฉันเห็นคือการใช้ this.view.Select () ในตัวควบคุม - การมีเพศสัมพันธ์แบบแน่นหนานี้เป็นสิ่งจำเป็นหรือมีวิธีการแยกส่วนเพิ่มเติม
wigglyworm

@wigglyworm - มี decoupling มากขึ้นเสมอ! : D แต่จริงๆแล้วคอนโทรลเลอร์ควรเป็นตัวควบคุมที่สื่อสารกับโมเดลจากนั้นอัพเดตมุมมองเพื่อให้การเชื่อมต่อส่วนใหญ่เกิดขึ้นใน MVC
Travis J

2

นี่คือสิ่งที่คุณทำผิดไปแล้ว - คุณได้ทำ MVC ด้วยมือขณะอยู่ในภาวะสับสนและไม่มี MVC ใด ๆ อยู่ใต้เข็มขัดของคุณ

ลองดูที่ PureMVC มันเป็นผู้ไม่เชื่อเรื่องภาษาและสามารถเป็นแพลตฟอร์มที่ดีในการทำให้เท้าของคุณเปียกด้วยการทำ MVC

รหัสของมันมีขนาดเล็กและเข้าใจได้และสิ่งนี้จะช่วยให้คุณปรับแต่งตามความต้องการของคุณในขณะที่คุณกำลังดำเนินการ

เริ่มเขียนเกมง่ายๆขนาดเล็กด้วยมันผู้กวาดทุ่นระเบิดน่าจะดี สิ่งที่เทรวิสเจพูดนั้นดีมากโดยเฉพาะเกี่ยวกับตัวแบบ ฉันจะเพิ่มเฉพาะที่คุณต้องจำไว้ว่าตัวควบคุม (อย่างน้อยใน PureMvc) เป็นคนไร้สัญชาติพวกเขาเข้ามามีชีวิตทำงานโดยสังเขปของพวกเขาและหายไป พวกเขาเป็นคนที่มีความรู้ พวกเขาเป็นเหมือนฟังก์ชั่น "เติมกริด, cuz เปลี่ยนโมเดล", "อัปเดตโมเดล, cuz ปุ่มถูกผลัก"

Views (ผู้ไกล่เกลี่ยใน PureMVC) นั้นโง่ที่สุดและ Model นั้นฉลาดกว่าเพียงเล็กน้อยเท่านั้น ทั้งนามธรรมนำไปปฏิบัติดังนั้นคุณ (ผู้ควบคุม) ไม่เคยสัมผัส UI หรือ DB โดยตรง

องค์ประกอบทั้งหมดของ UI ของคุณ (เช่นในแอป winforms) มีมุมมอง (ผู้ไกล่เกลี่ย - คุณเห็นว่าทำไมนี่จึงเป็นคำศัพท์ที่ดีกว่าตอนนี้หรือไม่) แต่ผู้ไกล่เกลี่ยสามารถสร้างเมตาที่เกี่ยวข้องเช่น "ควบคุมสี" หรือ "โฟกัส ผู้จัดการ "ซึ่งทำงานข้ามองค์ประกอบ UI คิดในเลเยอร์ที่นี่

กิจกรรม UI และ DB สามารถเรียกใช้ตัวควบคุมได้โดยอัตโนมัติ (ถ้าคุณใช้รูปแบบการตั้งชื่ออัจฉริยะ) และตัวควบคุมบางตัวสามารถเลิกใช้งานได้ - ผู้ไกล่เกลี่ยสามารถทำหน้าที่รับฟังการเปลี่ยนแปลงข้อมูลโมเดลโดยตรงและส่งแพคเกจข้อมูล

แม้ว่านี่จะเป็นสูตรโกงและต้องการให้ Model รู้เล็กน้อยเกี่ยวกับสิ่งที่อยู่ข้างนอกและผู้ไกล่เกลี่ยเข้าใจว่าจะทำอย่างไรกับแพ็คเกจข้อมูล

รุ่น: ใบ้ แต่สามารถนำกลับมาใช้ใหม่ได้; ตัวควบคุม: สมาร์ท แต่นำมาใช้ใหม่ได้น้อยลง (เป็นแอป); ผู้ไกล่เกลี่ย: เป็นใบ้ แต่นำมาใช้ซ้ำได้ การใช้ซ้ำได้ในกรณีนี้หมายถึงพกพาไปยังแอปอื่น

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.