พฤติกรรมการใช้งานในเกมผจญภัยง่าย ๆ


11

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

เพื่อให้ภาพรวมคร่าวๆ: เกมแบ่งย่อยเป็นRoomวัตถุ แต่ละRoomรายการมีEntityวัตถุที่อยู่ในห้องนั้น แต่ละคนEntityมีสถานะเหตุการณ์ซึ่งเป็นแผนที่แบบสตริง -> บูลีนอย่างง่ายและรายการการกระทำซึ่งเป็นแผนที่แบบสตริง -> ฟังก์ชั่น

[action] [entity]ท่านผู้ใช้แบบฟอร์มที่ใช้ Roomใช้ชื่อนิติบุคคลที่จะกลับมาที่เหมาะสมEntityวัตถุซึ่งจากนั้นจะใช้ชื่อการกระทำที่จะหาฟังก์ชั่นที่ถูกต้องและดำเนินการมัน

ในการสร้างคำอธิบายห้องพักแต่ละวัตถุแสดงสตริงคำอธิบายของตัวเองแล้วผนวกสตริงคำอธิบายของทุกRoom รายละเอียดอาจเปลี่ยนแปลงขึ้นอยู่กับสถานะของมัน ( "ประตูเปิด", "ประตูถูกปิด", "ประตูถูกล็อค" ฯลฯ )EntityEntity

นี่คือปัญหา: การใช้วิธีนี้จำนวนฟังก์ชั่นคำอธิบายและการกระทำที่ฉันต้องนำมาใช้อย่างรวดเร็ว ห้องเริ่มต้นของฉันคนเดียวมีประมาณ 20 หน้าที่ระหว่าง 5 เอนทิตี

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

แก้ไข 1: ตามที่ร้องขอตัวอย่างรหัสเทียมของฟังก์ชันการกระทำเหล่านี้

string outsideDungeonBushesSearch(currentRoom, thisEntity, player)
    if thisEntity["is_searched"] then
        return "There was nothing more in the bushes."
    else
        thisEntity["is_searched"] := true
        currentRoom.setEntity("dungeonDoorKey")
        return "You found a key in the bushes."
    end if

string dungeonDoorKeyUse(currentRoom, thisEntity, player)
    if getEntity("outsideDungeonDoor")["is_locked"] then
        getEntity("outsideDungeonDoor")["is_locked"] := false
        return "You unlocked the door."
    else
        return "The door is already unlocked."
    end if

ฟังก์ชั่นคำอธิบายทำหน้าที่เหมือนกันมากในการตรวจสอบสถานะและส่งคืนสตริงที่เหมาะสม

แก้ไข 2: แก้ไขข้อความของฉัน สมมติว่าอาจมีวัตถุในเกมจำนวนมากที่ไม่แชร์พฤติกรรมทั่วไป (การตอบสนองตามสถานะของการกระทำเฉพาะ) กับวัตถุอื่น มีวิธีที่ฉันสามารถกำหนดพฤติกรรมที่ไม่ซ้ำกันเหล่านี้ในวิธีที่สะอาดและบำรุงรักษาได้ดีกว่าการเขียนฟังก์ชั่นที่กำหนดเองสำหรับแต่ละการกระทำที่เฉพาะเจาะจงของเอนทิตี้หรือไม่?


1
ฉันคิดว่าคุณต้องอธิบายว่า "ฟังก์ชั่นการกระทำ" เหล่านี้ทำอะไรและอาจโพสต์โค้ดบางส่วนเพราะฉันไม่แน่ใจว่าคุณกำลังพูดถึงอะไรอยู่ที่นั่น
jhocking

เพิ่มรหัส
Eric

คำตอบ:


5

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

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

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

ในขณะเดียวกันหนึ่งในวิธีการสาธารณะคือสิ่งที่ต้องการ Entity.actOn (การกระทำของสตริง) จากนั้นในวิธีการนั้นเปรียบเทียบการกระทำที่ส่งผ่านกับตารางของการกระทำสำหรับวัตถุนั้น หากการกระทำนั้นอยู่ในตารางจากนั้นส่งคืนผลลัพธ์

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

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

<rooms>
  <room id="room1">
    <description>Outside the dungeon you see some bushes and a heavy door over the entrance.</description>
    <entities>
      <bush>
        <description>The bushes are thick and leafy.</description>
        <contains>
          <key />
        </contains>
      </bush>
      <door connection="room2" isLocked="true">
        <description>It's an oak door with stout iron clasps.</description>
      </door>
    </entities>
  </room>

  <room id="room2">
    etc.

เพิ่มเติม: aha ฉันเพิ่งอ่านคำตอบของ FxIII และบิตนี้ใกล้จะจบฉันก็:

(no things like <item triggerFlamesOnPicking="true"> that you will use just once)

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

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

<entity name="door">
  <description>It's an oak door with stout iron clasps.</description>
  <components>
    <lock isLocked="true" />
    <portal connection="room2" />
  </components>
</entity>

แต่ประตูเดียวกับกับดักลูกไฟจะเป็น

<entity name="door">
  <description>There are strange runes etched into the wood.</description>
  <components>
    <lock isLocked="true" />
    <portal connection="room7" />
    <fireballTrap />
  </components>
</entity>

แล้วรหัสเฉพาะเดียวที่ฉันจะต้องเขียนสำหรับประตูนั่นคือองค์ประกอบ FireballTrap มันจะใช้องค์ประกอบ Lock และ Portal เดียวกับประตูอื่น ๆ ทั้งหมดและถ้าฉันตัดสินใจใช้ FireballTrap บนหีบสมบัติหรืออะไรที่ง่ายพอ ๆ กับการเพิ่มส่วนประกอบ FireballTrap ลงในหีบนั้น

ไม่ว่าคุณจะกำหนดองค์ประกอบทั้งหมดในรหัสที่คอมไพล์หรือในภาษาสคริปต์แยกต่างหากไม่ใช่ความแตกต่างที่ยิ่งใหญ่ในใจของฉัน (ไม่ว่าคุณจะเขียนโค้ดที่ไหนสักแห่ง ) แต่สิ่งสำคัญคือคุณสามารถลด จำนวนรหัสเฉพาะที่คุณต้องเขียน เฮ้ถ้าคุณไม่กังวลเกี่ยวกับความยืดหยุ่นสำหรับนักออกแบบระดับ / modders (คุณกำลังเขียนเกมนี้ด้วยตัวเองหลังจากทั้งหมด) คุณสามารถทำให้เอนทิตีทั้งหมดสืบทอดจากเอนทิตี้และเพิ่มส่วนประกอบในคอนสตรัคเตอร์มากกว่าไฟล์ config หรือสคริปต์หรือ สิ่งที่:

Door extends Entity {
  public Door() {
    addComponent(new LockComponent());
    addComponent(new PortalComponent());
  }
}

TrappedDoor extends Entity {
  public TrappedDoor() {
    addComponent(new LockComponent());
    addComponent(new PortalComponent());
    addComponent(new FireballTrap());
  }
}

1
ใช้งานได้กับรายการทั่วไปที่ทำซ้ำได้ แต่สิ่งที่เกี่ยวกับเอนทิตีที่ตอบสนองต่อการป้อนข้อมูลของผู้ใช้โดยเฉพาะ? การสร้างคลาสย่อยของEntityเพียงสำหรับออบเจ็กต์เดี่ยวจัดกลุ่มโค้ดด้วยกัน แต่ไม่ลดจำนวนโค้ดที่ฉันต้องเขียน หรือว่าเป็นข้อผิดพลาดที่หลีกเลี่ยงไม่ได้ในเรื่องนี้?
Eric

1
ฉันพูดถึงตัวอย่างที่คุณให้ ฉันอ่านใจคุณไม่ได้ คุณต้องการมีวัตถุและอินพุตอะไร
jhocking

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

ใช่นั่นคือความคิด
jhocking

ฉันควรจะคิดว่าส่วนประกอบจะเป็นส่วนหนึ่งของการแก้ปัญหา ขอบคุณสำหรับความช่วยเหลือ
Eric

1

ปัญหาด้านมิติที่คุณกล่าวถึงค่อนข้างปกติและเกือบจะหลีกเลี่ยงไม่ได้ คุณต้องการที่จะหาวิธีที่จะแสดงหน่วยงานของคุณที่เป็นทั้งcoinciseและมีความยืดหยุ่น

"ตู้คอนเทนเนอร์" (พุ่มไม้ในคำตอบที่จับจ้อง) เป็นวิธีที่เหมือนกัน แต่คุณเห็นว่ามันไม่ยืดหยุ่นพอ

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

ข้อเสนอแนะของฉันคือการใช้แปลภาษากับพฤติกรรมรหัส

คิดเกี่ยวกับตัวอย่างพุ่มไม้: มันเป็นภาชนะ แต่พุ่มไม้ของเราจำเป็นต้องมีสิ่งของเฉพาะภายใน วัตถุคอนเทนเนอร์อาจมี:

  • วิธีสำหรับนักเล่าเรื่องที่จะเพิ่มรายการ
  • วิธีสำหรับเครื่องยนต์ในการแสดงรายการที่บรรจุอยู่
  • วิธีการสำหรับผู้เล่นที่จะเลือกรายการ

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

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

ตอนนี้คุณมีตัวเลือกสถาปัตยกรรมมากมาย: คุณสามารถกำหนดเครื่องมือเชิงพฤติกรรมเป็นคลาสพื้นฐานโดยใช้ภาษารหัสของคุณหรือภาษาสคริปต์ (สิ่งต่าง ๆ เช่นคอนเทนเนอร์, หน้าตาเหมือนประตูเป็นต้น) วัตถุประสงค์ของสิ่ง Theese คือการช่วยให้คุณสามารถอธิบายหน่วยงาน easely รวมพฤติกรรมที่เรียบง่ายและการกำหนดค่าโดยใช้ผูกกับภาษาสคริปต์

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

การใช้กลยุทธ์การเขียนสคริปต์ช่วยให้คุณสามารถกำหนดค่าของคุณได้ง่าย (ไม่มีสิ่งใดเหมือน<item triggerFlamesOnPicking="true">ที่คุณจะใช้เพียงครั้งเดียว) ในขณะที่ให้คุณแสดง beaviours แปลก ๆ (ความสนุก) เพิ่มโค้ด

พูดง่ายๆคือสคริปต์เป็นไฟล์ปรับแต่งที่สามารถเรียกใช้รหัสได้

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