در بازی Tale of Ronin، به دنبال طراحی یک سیستم مکالمه بودیم که حساس به بستر و شرایط بازی باشد به طوری که دیالوگ ها با توجه به متغیرها و رخدادهای دنیای بازی انتخاب و نمایش داده شوند. در واقع دیالوگی برای شروع انتخاب می شد که پیش شرط های لازم را می داشت، برای همین برای هر دیالوگ باید پیش شرطی با توجه به رخدادهای دنیای بازی نوشته می شد. همچنین برای ایجاد تنوع و شاخه های داستانی و همچنین شاخه های یک دیالوگ نیاز داشتیم تا شروطی را تعیین کنیم تا شاخه ها بر اساس حوادث داستانی و متغیرهای دنیا انتخاب شوند. برای همین نیاز به روشی برای ایجاد گزارههای شرطی داشتیم.
اولین ایده ای که به نظر می رسد این است که یک زبان اسکریپتی ساده برای ارزیابی گزارههای منطقی و شرطی ایجاد کنیم ولی این کار مشکلاتی را در پی دارد. چون نوشتن عبارات شرطی به صورت متنی و تفسیر کردن آن به ما اجازه دنبال کردن تغییرات را نمی داد چون عبارات شرطی فقط یک متن هستند و آگاهی از متغیرهای دنیای بازی ندارند. همچنین در صورت عدم توسعه ابزارهای کمکی برای نوشتن اسکریپت ها که امکان یافتن اشکالات اسکریپت حین نوشتن به کاربر بدهد، امکان تولید خطا در زمان اجرا زیاد بود و به دلیل ماهیت بازی، پیگیری این اشتباهات میتوانست وقت گیر باشد، از طرفی توسعه ابزار کارآمد خود نیازمند زمان و هزینه زیادی بود.
ایده ساده تر، نوشتن توابعی برای برسی همه اطلاعات مورد نیاز در زبان برنامه نویسی موتور بازی و expose کردن آن ها در سیستم تولید مکالمه بود. این راه شاید آسان تر از ایده قبل باشد (البته کاربر باید توانایی ترکیب این توابع برای تولید شرط های پیچیده تر را نیز داشته باشد که خود نیازمند تفسیر شرط نوشته شده خواهد بود)، ولی در صورتی که طراحان نیازمند برسی اطلاعات جدیدی در دنیا باشند، برنامه نویسان باید توابع جدیدی برای آن ها ایجاد و آن ها را در ابزار تولید دیالوگ قرار میداند که در این شرایط طراحان بازی به برنامه نویسان وابسته می شدند که به دلیل دور کاری بعضی از اعضای تیم و تفاوت منطقه زمانی، میتوانست در کار تولید محتوا وقفه ایجاد کند.
در نهایت با الهام گیری از دو ارائه فوق العاده [1] و [2] به راهکاری ساده برای رفع نیازمان رسیدیم که در ادامه به توضیح آن می پردازم.
سیستم Context، یک حافظه عمومی و واسطه ارتباط میان سیستم مکالمه و بازی است که اطلاعاتی مانند اطلاعات جهان بازی، اطلاعات کاراکترهای اصلی و فرعی، وضعیت ماموریت ها و ... در آن نگهداری میشود و سیستم های مختلف در بازی میتوانند در زمان اجرا این مقادیر را بخوانند و یا مقدار آن ها را تغییر دهند.
برای نظم دهی، این اطلاعات به مجموعه های مختلفی دسته بندی میشوند که به هر یک از این مجموعه ها Blackboard میگوییم. برای مثال اطلاعات بازیکن اصلی مانند میزان پول، سلامتی، غذا، سطح تجربه و ... در یک Blackboard به نام player و اطلاعات جهان بازی مانند ساعت بازی و ... در یک Blackboard به نام world نگهداری میشوند. سیستم های مختلف میتوانند با استفاده از کلیدی که ما به آن ContextKey میگوییم به این مقادیر دسترسی داشته باشند. یک ContextKey از دو قسمت نام Blackboard و نام مقدار تشکیل شده، برای مثال برای دسترسی به مقدار سلامتی بازیکن، کلید به صورت player:health
و برای دسترسی به زمان دنیا کلید به صورت world:time
خواهد بود.
از دید برنامه نویسی، یک Blackboard، یک دیکشنری بین نام مقادیر و اشاره گرهای به آن مقادیر و Context نیز یک دیکشنری بین نام Blackboard ها و اشاره گرهای به آنها است.
برای ساده سازی سیستم، مقادیری که در Blackboard نگهداری میشوند به نوع های string, int, float و bool محدود شده اند، همچنین برای جلوگیری از خطای تایپی و دنبال کردن راحتتر باگ ها، این مقادیر از قبل تعریف شده و در زمان اجرا مقادیر اضافه و یا حذف نمیشوند. البته یک استثنا وجود دارد، در حین یک مکالمه امکان دارد نیاز به ذخیره یک مقدار و استفاده از آن در همان مکالمه باشیم و بعد از اتمام مکالمه دیگر نیازی به آن مقدار نباشد. به این منظور یک Blackboard به نام Temp نیز در سیستم وجود دارد که محتویات آن ذخیره و بازیابی نمیشود و طراح میتواند در آن اطلاعاتی را حذف و اضاف کند.
نکته قابل ذکر، وجود کاراکترهای زیاد در بازی ToR است که نگهداری همزمان آن ها در سیستم باعث شلوغی و از دست دادن حافظه زیادی میشود. برای جلوگیری از این اتفاق، برای تمامی کاراکترهای NPC فقط یک Blackboard به نام Other وجود دارد که نام مقادیر و نوع آن ها از قبل تعریف شده اند، در هنگام اغاز یک مکالمه، اطلاعات کاراکتری که بازیکن قصد مکالمه با اون را دارد در این Blackboard ریخته شده، در حین مکالمه ممکن است این مقادیر توسط انتخاب هایی که بازیکن میکنند تغییر کنند، به همین دلیل بعد از پایان مکالمه اطلاعات کاراکتر با Blackboard همگام سازی میشود.
از نگاه سیستم دیالوگ یک RuleDB، یک شرط است، که در صورت صحت آن، یک مکالمه میتواند شروع شود و یا خط داستانی عوض شود.
فرض کنید برای شروع یک مکالمه با یک شخص، شرطی مانند زیر را داشته باشیم:
همانگونه که مشاهده میکنید، مکالمه زمانی شروع میشود که:
"مقدار Who برابر با Akira باشد و مقدار Level بزرگتر از 10"
یا
"مقدار Who برابر با Akira باشد و مقدار Food کمتر از 2".
همانطور که مشخص است، این شرط از دو بخش تشکیل شده که با هم or میشوند، در هر یک از بخش ها نیز دو گزاره وجود دارد که با یکدیگر and میشوند.
در RuleDb به گزاره "مقدار Who برابر با Akira باشد" یک Criteria میگوییم. یک Criteria از نام مقدار مورد نظر در Context یا همان ContextKey، علمگر مقایسه و مقدار مورد انتظار تشکیل میشود.
در هنگام ارزیابی، Criteria مقدار مورد نظری که توسط ContextKey مشخص شده است را از سیستم Context دریافت کرده و با توجه یه عملگر داده شده، آن را با مقدار مورد انتظار مقایسه میکند که نتیجه آن میتواند صحیح و یا غلط باشد.
گزاره دوم بخش اول را نیز میتواند به صورت زیر نوشت:
برای and کردن این دو گزاره، از Rule استفاده میکنیم، یک Rule مجموعه ای از Criteria ها است که در هنگام ارزیابی، تمامی Criteria های آن با یکدیگر and میشوند. یک Rule زمانی صحیح است که تمامی Criteria های تشکیل دهنده آن صحیح باشد و یا به عبارت دیگر، مقدار هیچ Criteria ای در آن غلط نباشد. یک Rule را میتوان برابر با عبارت هورن در برنامه نویسی منطق دانست.
تا به حال بخش اول را ایجاد کردیم. بخش دوم را نیز به سادگی میتواند به صورت یک Rule نوشت:
برای Or کردن این دو Rule، از RuleDB استفاده میکنیم. یک RuleDB مجموعه ای از Rule ها است که در هنگام ارزیابی تمامی Rule های تشکیل دهنده آن با یکدیگر or میشوند. یک RuleDB زمانی صحیح است که یکی از Rule های آن صحیح باشد.
به همین سادگی شرط مورد نظر تبدیل به یک RuleDB شد که از آن میتوان در سیستم مکالمه استفاده کرد.
در بازی ToR، یک سیستم Node Base برای ساخت مکالمات توسعه داده ایم، Node هایی که نیاز به شرط دارند (مانند Node آغازین یک مکالمه) شامل نمونه ای از RuleDB هستند که طراح میتواند در پنجره مربوطه شرط دلخواهش را تولید کند. برای ساده نگهداشتن سیستم و خوانایی بهتر، مکالمات به صورت Json ذخیره و بازیابی میشوند.
در بازی ToR ما از RuleDB به عنوان گزاره شرطی استفاده کردیم، ولی این سیستم، کاربردهای فراوان دیگری میتواند داشته باشد. بعضی کاربردهای دیگر در ارائه های [1] و [2] توضیح داده شده اند که پیشنهاد میکنم حتما آن ها را مشاهده کنید.
1- AI-driven Dynamic Dialog through Fuzzy Pattern Matching - Elan Ruskin - 2012 - Valve
2- Programming Context-Aware Dialog in The Last of Us - Jason Gregory - 2014 - Naughty Dog