Navid Bigdeli
Navid Bigdeli
خواندن ۶ دقیقه·۵ سال پیش

تولید گزاره شرطی با استفاده از Rule System

در بازی Tale of Ronin، به دنبال طراحی یک سیستم مکالمه بودیم که حساس به بستر و شرایط بازی باشد به طوری که دیالوگ ها با توجه به متغیرها و رخدادهای دنیای بازی انتخاب و نمایش داده شوند. در واقع دیالوگی برای شروع انتخاب می شد که پیش شرط های لازم را می داشت، برای همین برای هر دیالوگ باید پیش شرطی با توجه به رخدادهای دنیای بازی نوشته می شد. همچنین برای ایجاد تنوع و شاخه های داستانی و همچنین شاخه های یک دیالوگ نیاز داشتیم تا شروطی را تعیین کنیم تا شاخه ها بر اساس حوادث داستانی و متغیرهای دنیا انتخاب شوند. برای همین نیاز به روشی برای ایجاد گزاره‌های شرطی داشتیم.

اولین ایده ای که به نظر می رسد این است که یک زبان اسکریپتی ساده برای ارزیابی گزاره‌های منطقی و شرطی ایجاد کنیم ولی این کار مشکلاتی را در پی دارد. چون نوشتن عبارات شرطی به صورت متنی و تفسیر کردن آن به ما اجازه دنبال کردن تغییرات را نمی داد چون عبارات شرطی فقط یک متن هستند و آگاهی از متغیرهای دنیای بازی ندارند. همچنین در صورت عدم توسعه ابزارهای کمکی برای نوشتن اسکریپت ها که امکان یافتن اشکالات اسکریپت حین نوشتن به کاربر بدهد، امکان تولید خطا در زمان اجرا زیاد بود و به دلیل ماهیت بازی، پیگیری این اشتباهات میتوانست وقت گیر باشد، از طرفی توسعه ابزار کارآمد خود نیازمند زمان و هزینه زیادی بود.

ایده ساده تر، نوشتن توابعی برای برسی همه اطلاعات مورد نیاز در زبان برنامه نویسی موتور بازی و expose کردن آن ها در سیستم تولید مکالمه بود. این راه شاید آسان تر از ایده قبل باشد (البته کاربر باید توانایی ترکیب این توابع برای تولید شرط های پیچیده تر را نیز داشته باشد که خود نیازمند تفسیر شرط نوشته شده خواهد بود)، ولی در صورتی که طراحان نیازمند برسی اطلاعات جدیدی در دنیا باشند، برنامه نویسان باید توابع جدیدی برای آن ها ایجاد و آن ها را در ابزار تولید دیالوگ قرار میداند که در این شرایط طراحان بازی به برنامه نویسان وابسته می شدند که به دلیل دور کاری بعضی از اعضای تیم و تفاوت منطقه زمانی، میتوانست در کار تولید محتوا وقفه ایجاد کند.

در نهایت با الهام گیری از دو ارائه فوق العاده [1] و [2] به راهکاری ساده برای رفع نیازمان رسیدیم که در ادامه به توضیح آن می پردازم.

Context System:

سیستم 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 همگام سازی میشود.

Rule Database:

از نگاه سیستم دیالوگ یک RuleDB، یک شرط است، که در صورت صحت آن، یک مکالمه میتواند شروع شود و یا خط داستانی عوض شود.

فرض کنید برای شروع یک مکالمه با یک شخص، شرطی مانند زیر را داشته باشیم:

https://gist.github.com/navidbigdeli54/3f683b0e6991b362441c9fe229a8b999

همانگونه که مشاهده میکنید، مکالمه زمانی شروع میشود که:

"مقدار Who برابر با Akira باشد و مقدار Level بزرگتر از 10"

یا

"مقدار Who برابر با Akira باشد و مقدار Food کمتر از 2".

همانطور که مشخص است، این شرط از دو بخش تشکیل شده که با هم or میشوند، در هر یک از بخش ها نیز دو گزاره وجود دارد که با یکدیگر and میشوند.

در RuleDb به گزاره "مقدار Who برابر با Akira باشد" یک Criteria میگوییم. یک Criteria از نام مقدار مورد نظر در Context یا همان ContextKey، علمگر مقایسه و مقدار مورد انتظار تشکیل میشود.

https://gist.github.com/navidbigdeli54/53067250fe2c3292e79cf661634521ff

در هنگام ارزیابی، Criteria مقدار مورد نظری که توسط ContextKey مشخص شده است را از سیستم Context دریافت کرده و با توجه یه عملگر داده شده، آن را با مقدار مورد انتظار مقایسه میکند که نتیجه آن میتواند صحیح و یا غلط باشد.

گزاره دوم بخش اول را نیز میتواند به صورت زیر نوشت:

https://gist.github.com/navidbigdeli54/82a7b8125bb9eb0cdc8e11ebedf6e61f

برای and کردن این دو گزاره، از Rule استفاده میکنیم، یک Rule مجموعه ای از Criteria ها است که در هنگام ارزیابی، تمامی Criteria های آن با یکدیگر and میشوند. یک Rule زمانی صحیح است که تمامی Criteria های تشکیل دهنده آن صحیح باشد و یا به عبارت دیگر، مقدار هیچ Criteria ای در آن غلط نباشد. یک Rule را میتوان برابر با عبارت هورن در برنامه نویسی منطق دانست.

https://gist.github.com/navidbigdeli54/35c93c6190d227609419c4145e18b370

تا به حال بخش اول را ایجاد کردیم. بخش دوم را نیز به سادگی میتواند به صورت یک Rule نوشت:

https://gist.github.com/navidbigdeli54/22e7b9771415994d9112de5f8da09446

برای Or کردن این دو Rule، از RuleDB استفاده میکنیم. یک RuleDB مجموعه ای از Rule ها است که در هنگام ارزیابی تمامی Rule های تشکیل دهنده آن با یکدیگر or میشوند. یک RuleDB زمانی صحیح است که یکی از Rule های آن صحیح باشد.

https://gist.github.com/navidbigdeli54/6be0f68bfe59a622fccd443003310634

به همین سادگی شرط مورد نظر تبدیل به یک 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

برنامه نویسیتوسعه بازیمهندسی نرم افزار
Lead Software Engineer @ Dead Mage Studio
شاید از این پست‌ها خوشتان بیاید