DEV Community

Nuttapon-Sartmanee
Nuttapon-Sartmanee

Posted on

เครื่องมือการสร้างแผนที่ในเกมโดยใช้ Random Walk Algorithm

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

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

เราจะใช้ Random Walk Algorithm ในการสร้างแผนที่นี้ โดยจำเป็นต้องมีแผนที่กว้าง ๆ ในรูปแบบของ Grid ก่อนจึงจะสามารถใช้งาน Algorithm ได้ เริ่มต้นจะมีการสุ่มจุดใดจุดหนึ่งบนแผนที่ก่อน จากนั้นก็จะเริ่มสร้างอุโมงค์ด้วยการสุ่ม จนกระทั่งได้จำนวนอุโมงค์ตามที่ต้องการ โดยเราสามารถกำหนดค่าให้กับ Algorithm นี้ได้ คือ

  1. Dimensions (ความกว้างและความสูงของแผนที่)
  2. MaxTunnels (จำนวนรอบสูงสุดที่อัลกอริทึมสามารถทำได้ขณะสร้างแผนที่)
  3. MaxLength (ความยาวสูงสุดของแต่ละอุโมงค์ที่อัลกอริทึมจะเลือกก่อนทำการเลี้ยวในแนวนอนหรือแนวตั้ง)

การทำงานของ Algorithm จะมีดังนี้

  1. สร้างแผนที่สองมิติในรูปแบบของ Grid
  2. เลือกจุดเริ่มต้นแบบสุ่มบนแผนที่
  3. จำนวนอุโมงค์ต้องไม่เป็นศูนย์
  4. สุ่มความยาวจากความยาวสูงสุดที่ผู้ใช้กำหนด
  5. สุ่มทิศทางการเลี้ยว (ขวา, ซ้าย, ขึ้น, ลง)
  6. วาดอุโมงค์ในทิศทางนั้นโดยหลีกเลี่ยงขอบของแผนที่
  7. ลดจำนวน tunnels และทำวนซ้ำไปเรื่อย ๆ
  8. ส่งข้อมูลแผนที่ที่สร้างเสร็จแล้วกลับไป

เนื่องจากแผนที่ประกอบด้วยอุโมงค์และกำแพง จึงสามารถอธิบายเป็น 0 และ 1 ในอาร์เรย์สองมิติดังต่อไปนี้ (1 หมายถึง "กำแพง" , 0 หมายถึง "อุโมงค์")

map = [[1,1,1,1,0],
       [1,0,0,0,0],
       [1,0,1,1,1],       
       [1,0,0,0,1],       
       [1,1,1,0,1]]
Enter fullscreen mode Exit fullscreen mode

เราสามารถเข้าถึงค่าของมันได้โดยการใช้แถวและคอลัมน์ เช่น map [row] [column]

ก่อนเขียนอัลกอริทึม ต้องมีฟังก์ชันตัวช่วยในการสร้างอาร์เรย์สองมิติ คือ

createArray(num, dimensions) {
    var array = [];    
    for (var i = 0; i < dimensions; i++) { 
      array.push([]);      
      for (var j = 0; j < dimensions; j++) {  
         array[i].push(num);      
      }    
    }    
    return array;  
}
Enter fullscreen mode Exit fullscreen mode

สร้างตัวแปรต่าง ๆ เพื่อเตรียมกำหนดค่าให้กับ Random Walk Algorithm

createMap(){
 let dimensions = 5,     
 maxTunnels = 3, 
 maxLength = 3;
Enter fullscreen mode Exit fullscreen mode

สร้างอาร์เรย์สองมิติโดยใช้ฟังก์ชันตัวช่วยในการสร้างอาร์เรย์สองมิติ ที่กำหนดไว้ล่วงหน้า

let map = createArray(1, dimensions);
Enter fullscreen mode Exit fullscreen mode

สุ่มแถวและคอลัมน์ เพื่อเป็นจุดเริ่มต้นบนแผนที่

let currentRow = Math.floor(Math.random() * dimensions),       
    currentColumn = Math.floor(Math.random() * dimensions);
Enter fullscreen mode Exit fullscreen mode

สร้างทิศทางการเคลื่อนที่ทั้งหมดที่ทำได้ ตัวอย่างเช่น หากต้องการไปที่จุดรอบ ๆ จุด [2][2] สามารถทำได้โดยวิธีต่อไปนี้

  • หากต้องการขึ้น ให้ลบ 1 ออกจากแถว คือไปที่จุด [1][2]
  • หากต้องการลงไป ให้เพิ่ม 1 ในแถว คือไปที่จุด [3][2]
  • หากต้องการไปทางขวา เพิ่ม 1 ในคอลัมน์ คือไปที่จุด [2][3]
  • หากต้องการไปทางซ้าย ให้ลบ 1 ออกจากคอลัมน์ คือไปที่จุด [2][1] Image description จะได้ทิศทางทั้งหมดเป็น
let directions = [[-1, 0], [1, 0], [0, -1], [0, 1]];
Enter fullscreen mode Exit fullscreen mode

สร้างตัวแปรเพื่อเก็บค่าสุ่มจาก การสุ่มทิศทางการเคลื่อนที่ และสร้างตัวแปรเพื่อเก็บ ทิศทางที่เคยเดินแล้ว โดยเริ่มต้นจะเป็น empty

let lastDirection = [], 
    randomDirection;
Enter fullscreen mode Exit fullscreen mode

สร้างเงื่อนไขที่ไม่ทำให้เกิดการเดินซ้ำที่เดิม

do {         
randomDirection = directions[Math.floor(Math.random() * directions.length)];      
} while ((randomDirection[0] === -lastDirection[0] &&    
          randomDirection[1] === -lastDirection[1]) || 
         (randomDirection[0] === lastDirection[0] &&  
          randomDirection[1] === lastDirection[1]));
Enter fullscreen mode Exit fullscreen mode

สร้างตัวแปรเพิ่มสุ่มความยาวจาก maxLength และใส่ค่าให้ tunnelLength เป็น 0

let randomLength = Math.ceil(Math.random() * maxLength),       
    tunnelLength = 0;
Enter fullscreen mode Exit fullscreen mode

สร้างอุโมงค์โดยเปลี่ยนค่าของช่องนั้นจากหนึ่งเป็นศูนย์โดยที่ tunnelLength น้อยกว่า randomLength และถ้าอุโมงค์ชนขอบแผนที่ จะหยุดการทำงาน

while (tunnelLength < randomLength) { 
 if(((currentRow === 0) && (randomDirection[0] === -1))||  
    ((currentColumn === 0) && (randomDirection[1] === -1))|| 
    ((currentRow === dimensions — 1) && (randomDirection[0] ===1))||
 ((currentColumn === dimensions — 1) && (randomDirection[1] === 1)))   
 { break; }
Enter fullscreen mode Exit fullscreen mode
else{ 
  map[currentRow][currentColumn] = 0; 
  currentRow += randomDirection[0];
  currentColumn += randomDirection[1]; 
  tunnelLength++; 
 } 
}
Enter fullscreen mode Exit fullscreen mode

หากอุโมงค์เกิดการชนขอบ จะเช็คว่าทิศทางนั้นมีความยาวอย่างน้อยหนึ่งหรือไม่ ถ้าใช่ ให้เปลี่ยนค่า LastDirection เป็น RandomDirection และลดค่า maxTunnels แล้วกลับไปสร้างอุโมงค์ใหม่ด้วยทิศทางสุ่มอื่น

if (tunnelLength) { 
 lastDirection = randomDirection; 
 maxTunnels--; 
}
Enter fullscreen mode Exit fullscreen mode

เมื่อเสร็จสิ้นการวาดอุโมงค์และ maxTunnels เป็นศูนย์ จะส่งข้อมูลแผนที่
ผลลัพธ์พร้อมทางเลี้ยวและอุโมงค์ทั้งหมดกลับไป

}
 return map;
};
Enter fullscreen mode Exit fullscreen mode

ตัวอย่างผลที่ได้จาก Code

Image description

สรุปผล

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

References: https://www.freecodecamp.org/news/how-to-make-your-own-procedural-dungeon-map-generator-using-the-random-walk-algorithm-e0085c8aa9a/

Top comments (0)