การออกแบบเกมเทิร์นเบสที่การกระทำมีผลข้างเคียง


19

ผมเขียนรุ่นคอมพิวเตอร์ของเกมการปกครอง มันเป็นเกมไพ่เทิร์นเบสที่การ์ดแอคชั่นการ์ดขุมทรัพย์และการ์ดแต้มชัยชนะจะถูกสะสมไว้ในเด็คส่วนบุคคลของผู้เล่น ฉันมีโครงสร้างของชั้นเรียนที่พัฒนาค่อนข้างดีและฉันเริ่มออกแบบตรรกะของเกม ฉันใช้หลามและฉันอาจเพิ่ม GUI อย่างง่ายด้วย pygame ในภายหลัง

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

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

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


2
สวัสดี Apis Utilis และยินดีต้อนรับสู่ GDSE คำถามของคุณเขียนได้ดีและดีมากที่คุณอ้างอิงคำถามที่เกี่ยวข้อง อย่างไรก็ตามคำถามของคุณครอบคลุมปัญหาต่าง ๆ มากมายและเพื่อให้ครอบคลุมทั้งหมดคำถามอาจจะต้องมีขนาดใหญ่มาก คุณยังอาจได้รับคำตอบที่ดี แต่ตัวคุณเองและเว็บไซต์จะได้รับประโยชน์หากคุณพบปัญหาเพิ่มเติม อาจเริ่มต้นด้วยการสร้างเกมที่ง่ายขึ้นและสร้างอาณาจักรขึ้นมา?
michael.bartnett

1
ฉันจะเริ่มต้นจากการให้สคริปต์แต่ละการ์ดที่ปรับเปลี่ยนสถานะของเกมและหากไม่มีอะไรแปลกประหลาดเกิดขึ้นถอยกลับไปที่กฎการเลี้ยวเป็นค่าเริ่มต้น ...
Jari Komppa

คำตอบ:


11

ฉันเห็นด้วยกับ Jari Komppa ว่าการกำหนดเอฟเฟกต์การ์ดด้วยภาษาสคริปต์ที่ทรงพลังเป็นวิธีที่จะไป แต่ฉันเชื่อว่ากุญแจสำคัญในความยืดหยุ่นสูงสุดคือการจัดการเหตุการณ์ที่สามารถสคริปต์ได้

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

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

การ์ดเปิดกลับของคุณจะทำสิ่งนี้:

add_event_hook('cleanup_phase_end', current_player, function {
     setNextPlayer(current_player); // make the player take another turn
     return false; // unregister this hook afterwards
});

(ฉันไม่รู้ว่า Dominion มีบางอย่างเช่น "cleanup phase" - ในตัวอย่างนี้มันเป็นช่วงสุดท้ายของการเปลี่ยนผู้เล่น)

การ์ดที่อนุญาตให้ผู้เล่นทุกคนจั่วการ์ดเพิ่มเติมที่จุดเริ่มต้นของเฟสการดึงของพวกเขาจะเป็นดังนี้:

add_event_hook('draw_phase_begin', NULL, function {
    drawCard(current_player); // draw a card
    return true; // keep doing this until the hook is removed explicitely
});

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

add_event_hook('play_card', target_player, function {
    changeHitPoints(target_player, -1); // remove a hit point
    return true; 
});

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

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


1
ว้าว. สิ่งที่สับสนวุ่นวาย
Jari Komppa

คำตอบที่ดีเลิศ @ ฟิลิปและนี่จะช่วยจัดการสิ่งต่าง ๆ ในโดมิเนียน อย่างไรก็ตามมีการกระทำที่ต้องเกิดขึ้นทันทีเมื่อมีการเล่นไพ่เช่นเล่นไพ่ที่บังคับให้ผู้เล่นคนอื่นพลิกไพ่บนห้องสมุดของเขาและอนุญาตให้ผู้เล่นปัจจุบันพูดว่า "Keep it" หรือ "ทิ้งมัน" คุณจะเขียน hooks กิจกรรมเพื่อดูแลการดำเนินการในทันทีหรือคุณต้องการวิธีการเพิ่มเติมในการเขียนสคริปต์การ์ด
ฟอร์ด

2
เมื่อสิ่งที่ต้องการเกิดขึ้นทันทีสคริปต์ควรเรียกใช้ฟังก์ชันที่เหมาะสมโดยตรงและไม่ลงทะเบียนฟังก์ชัน hook
ฟิลิปป์

@ JariKomppa: ชุด Unglued เป็นเรื่องไร้สาระโดยเจตนาและเต็มไปด้วยไพ่บ้าที่ไม่สมเหตุสมผล สิ่งที่ฉันชอบคือการ์ดที่ทำให้ทุกคนได้รับความเสียหายเมื่อพวกเขาพูดคำเฉพาะ ฉันเลือก 'the'
Jack Aidley

9

ฉันให้ปัญหานี้ - เอ็นจิ้นเกมการ์ดที่ยืดหยุ่นด้วยคอมพิวเตอร์ - บางคนคิดว่าเมื่อสักครู่ที่ผ่านมา

ก่อนอื่นเกมไพ่ที่ซับซ้อนเช่น Chez Geek หรือ Fluxx (และฉันเชื่อว่า Dominion) จะต้องใช้การ์ดในการเขียนสคริปต์ โดยทั่วไปการ์ดแต่ละใบจะมาพร้อมกับสคริปต์ของตัวเองซึ่งอาจเปลี่ยนสถานะของเกมในรูปแบบต่างๆ สิ่งนี้จะช่วยให้คุณสามารถให้ระบบพิสูจน์อักษรในอนาคตได้เนื่องจากสคริปต์อาจทำสิ่งที่คุณไม่สามารถคิดได้ในตอนนี้ แต่อาจเกิดการขยายตัวในอนาคต

ประการที่สอง "การเลี้ยว" ที่เข้มงวดอาจทำให้เกิดปัญหา

คุณต้องมี "เทิร์นสแต็ค" ซึ่งมี "เทิร์นพิเศษ" บางอย่างเช่น "ทิ้งการ์ด 2 ใบ" เมื่อสแต็กว่างเปล่าเทิร์นปกติที่เป็นค่าเริ่มต้นจะดำเนินต่อไป

ใน Fluxx มีความเป็นไปได้ทั้งหมดที่การเลี้ยวหนึ่งครั้งจะเป็นเช่น:

  • เลือกการ์ด N (ตามที่ระบุไว้ในกฎปัจจุบันสามารถเปลี่ยนแปลงได้ผ่านการ์ด)
  • เล่นการ์ด N (ตามที่ระบุไว้ในกฎปัจจุบันสามารถเปลี่ยนแปลงได้ผ่านการ์ด)
    • หนึ่งในการ์ดอาจเป็น "ใช้ 3 เล่น 2 ของพวกเขา"
      • หนึ่งในการ์ดเหล่านั้นอาจเป็น "หันกลับ"
    • หนึ่งในการ์ดอาจเป็น "ทิ้งและวาด"
  • หากคุณเปลี่ยนกฎเพื่อเลือกไพ่มากกว่าที่คุณเคยทำเมื่อถึงตาคุณเริ่มให้เลือกไพ่เพิ่ม
  • หากคุณเปลี่ยนกฎสำหรับไพ่ในมือน้อยคนอื่น ๆ จะต้องทิ้งการ์ดทันที
  • เมื่อเทิร์นของคุณจบลงให้ทิ้งการ์ดจนกว่าคุณจะมีการ์ด N การ์ด (สามารถเปลี่ยนได้ผ่านการ์ดอีกครั้ง) จากนั้นจึงเลี้ยวอีกครั้ง (หากคุณเล่น

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

โดยพื้นฐานแล้วฉันจะเริ่มจากการออกแบบโครงสร้างการเลี้ยวที่ยืดหยุ่นมากออกแบบเพื่อให้สามารถอธิบายเป็นสคริปต์ได้ (เนื่องจากแต่ละเกมจะต้องมี "สคริปต์หลัก" ของตัวเองในการจัดการโครงสร้างเกมพื้นฐาน) จากนั้นการ์ดใด ๆ ควรเป็นสคริปต์ การ์ดส่วนใหญ่อาจไม่ได้ทำอะไรแปลก ๆ แต่คนอื่นทำ การ์ดสามารถมีคุณลักษณะต่าง ๆ - ไม่ว่าจะสามารถเก็บไว้ในมือเล่น "เมื่อใดก็ตาม" ไม่ว่าจะสามารถเก็บไว้เป็นสินทรัพย์ (เช่น fluxx 'เฝ้า' หรือสิ่งต่าง ๆ ใน 'chez geek' เหมือนอาหาร) ...

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


นี่เป็นคำตอบที่ดีมากและฉันจะยอมรับทั้งสองอย่างถ้าทำได้ ฉันยากจนผูกโดยการยอมรับคำตอบโดยบุคคลที่มีชื่อเสียงที่ต่ำกว่า :)
Apis Utilis

ไม่มีปัญหาฉันเคยชินกับมันตอนนี้ .. =)
Jari Komppa

0

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

แก้ไข: ECS อาจไม่จำเป็นต้องขึ้นอยู่กับชนิดของความยืดหยุ่นและการเพิ่มประสิทธิภาพที่คุณต้องการ เป็นเพียงวิธีหนึ่งในการบรรลุเป้าหมายนี้ DOD ฉันคิดผิดว่าเป็นขั้นตอนการเขียนโปรแกรมแม้ว่าพวกเขาจะเกี่ยวข้องมาก ที่ฉันหมายถึงคือ. คุณควรพิจารณาดำเนินการกับ OOP ทั้งหมดหรืออย่างน้อยที่สุดและให้ความสนใจกับข้อมูลและวิธีการจัดระเบียบแทน หลีกเลี่ยงการสืบทอดและวิธีการ แทนที่จะมุ่งเน้นไปที่ฟังก์ชั่นสาธารณะ (ระบบ) เพื่อจัดการข้อมูลบัตรของคุณ แต่ละการกระทำไม่ได้เป็นเทมเพลตหรือตรรกะใด ๆ แต่เป็นข้อมูลดิบแทน ตำแหน่งที่ระบบของคุณใช้เพื่อดำเนินการตรรกะ ตัวสลับจำนวนเต็มหรือการใช้จำนวนเต็มเพื่อเข้าถึงอาเรย์ของตัวชี้ฟังก์ชันช่วยคำนวณตรรกะที่ต้องการจากข้อมูลอินพุตได้อย่างมีประสิทธิภาพ

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

มีประโยชน์ที่จะทำเช่นนี้ การ์ดแต่ละใบอาจมีค่าหรือสตริง enum (s) เพื่อแสดงถึงการกระทำของพวกเขา ฝึกงานนี้ช่วยให้คุณสามารถออกแบบการ์ดผ่านไฟล์ข้อความหรือ json และอนุญาตให้โปรแกรมนำเข้าโดยอัตโนมัติ หากคุณทำการกระทำของผู้เล่นรายการของข้อมูลนี้ให้ความยืดหยุ่นมากยิ่งขึ้นโดยเฉพาะอย่างยิ่งถ้าการ์ดขึ้นอยู่กับตรรกะในอดีตเช่น Hearthstone ทำหรือถ้าคุณต้องการบันทึกเกมหรือเล่นซ้ำเกมได้ทุกที่ มีศักยภาพในการสร้าง AI ได้ง่ายขึ้น โดยเฉพาะอย่างยิ่งเมื่อใช้ "ระบบยูทิลิตี้" แทน "ต้นไม้พฤติกรรม" การสร้างเครือข่ายก็ง่ายขึ้นเพราะแทนที่จะต้องคิดหาวิธีที่จะทำให้วัตถุ polymorphic ทั้งหมดโอนผ่านสายและวิธีการจัดลำดับที่จะติดตั้งหลังจากความจริง การที่คุณมีวัตถุในเกมของคุณแล้วไม่ได้เป็นอะไรมากไปกว่าข้อมูลธรรมดา ๆ และสุดท้าย แต่ไม่ท้ายสุดนี้ช่วยให้คุณปรับแต่งได้ง่ายขึ้นเพราะแทนที่จะเสียเวลากังวลเกี่ยวกับรหัสของคุณสามารถจัดระเบียบข้อมูลของคุณได้ดีขึ้นดังนั้นโปรเซสเซอร์จะมีเวลาจัดการกับมันได้ง่ายขึ้น Python อาจมีปัญหาที่นี่ แต่ค้นหา "cache line" และความเกี่ยวข้องกับ dev ของเกม ไม่สำคัญสำหรับการทำต้นแบบ แต่บางทีอาจเป็นเรื่องใหญ่ที่จะเกิดประโยชน์

ลิงก์ที่มีประโยชน์บางอย่าง

หมายเหตุ: ECS อนุญาตให้หนึ่งเพิ่ม / ลบตัวแปร (เรียกว่าส่วนประกอบ) แบบไดนามิกที่รันไทม์ ตัวอย่างโปรแกรมที่แสดงให้เห็นว่า ECS "อาจ" มองอย่างไร (มีหลายวิธีในการทำ)

unsigned int textureID = ECSRegisterComponent("texture", sizeof(struct Texture));
unsigned int positionID = ECSRegisterComponent("position", sizeof(struct Point2DI));
for (unsigned int i = 0; i < 10; i++) {
    void *newEnt = ECSGetNewEntity();
    struct Point2DI pos = { 0 + i * 64, 0 };
    struct Texture tex;
    getTexture("test.png", &tex);
    ECSAddComponentToEntity(newEnt, &pos, positionID);
    ECSAddComponentToEntity(newEnt, &tex, textureID);
}
void *ent = ECSGetParentEntity(textureID, 3);
ECSDestroyEntity(ent);

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

unsigned int textureCount;
unsigned int positionID = ECSGetComponentTypeFromName("position");
unsigned int textureID = ECSGetComponentTypeFromName("texture");
struct Texture *textures = ECSGetAllComponentsOfType(textureID, &textureCount);
for (unsigned int i = 0; i < textureCount; i++) {
    void *parentEntity = ECSGetParentEntity(textureID, i);
    struct Point2DI *drawPos = ECSGetComponentFromEntity(positionID, parentEntity);
    if (drawPos) {
        struct Texture *t = &textures[i];
        drawTexture(t, drawPos->x, drawPos->y);
    }
}

1
คำตอบนี้จะดีกว่าหากมีรายละเอียดเพิ่มเติมเกี่ยวกับวิธีที่คุณแนะนำให้ตั้งค่า ECS ที่มุ่งเน้นข้อมูลของคุณและใช้เพื่อแก้ไขปัญหาเฉพาะนี้
DMGregory

อัปเดตขอบคุณสำหรับการชี้ให้เห็นว่า
Blue_Pyro

โดยทั่วไปแล้วฉันคิดว่าการบอกวิธี "ตั้งค่า" วิธีนี้เป็นสิ่งที่ไม่ดี แต่ให้พวกเขาออกแบบวิธีแก้ปัญหาของตัวเองแทน มันพิสูจน์ให้เห็นว่าทั้งสองเป็นวิธีที่ดีในการฝึกฝนและช่วยให้สามารถแก้ไขปัญหาได้ดียิ่งขึ้น เมื่อคิดเกี่ยวกับข้อมูลมากกว่าตรรกะในลักษณะนี้มันก็คือการมีหลายวิธีในการทำสิ่งเดียวกันและทั้งหมดขึ้นอยู่กับความต้องการของแอปพลิเคชัน รวมถึงเวลาโปรแกรมเมอร์ / ความรู้
Blue_Pyro
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.