برنامه نویسی

نحوه نوشتن اسکریپت های lua برای بازی های ویدیویی با شبیه ساز Bizhawk

Bizhawk یک شبیه ساز چند منظوره است که مورد علاقه جامعه Speedrun Assisted برای ابزارهای ضبط/پخش و اشکال زدایی و همچنین قابلیت های اسکریپت LUA است که می تواند برای اهداف مختلفی استفاده شود.

در حالی که مستندات اساسی وجود دارد که برخی از کارکردهای موجود در این اسکریپت ها را توصیف می کند ، کمبود نمونه کد کار ممکن است برای شروع برخی دشوار باشد. بیایید برخی از ویژگی های اسکریپت Lua را که شبیه ساز Bizhawk ارائه می دهد ، طی کنیم و با نمونه های واقعی از آن لذت ببریم.

راه اندازی شبیه ساز Bizhawk

Bizhawk در چندین سیستم عامل اجرا می شود ، اما اسکریپت Lua فقط در نسخه های ویندوز موجود است. اگر در Mac یا Linux هستید ، می توانید از شراب استفاده کنید ، اگرچه بسته به اینکه کدام نسخه از سیستم عامل مورد استفاده شما استفاده می کنید ، ممکن است کمی فرآیند درگیر باشد.

دستورالعمل های نصب در Readme مخزن پروژه وجود دارد ، از جمله نصب کننده ای که از پیش نیازها مراقبت می کند. بارگیری ، onzip ، این ابزار نصب را اجرا کنید ، و سپس نسخه مربوط به شبیه ساز را از Tasvideos بارگیری کنید. اکنون می توانید Emuhawk را اجرا کنید و بازی ویدیویی مورد علاقه خود را ROM بارگذاری کنید!

BROS اصلی Super Mario برای سیستم سرگرمی Nintendo یک بازی کلاسیک بسیار محبوب است ، بنابراین ما در طول پست برای نمونه های کد خود به آن مراجعه خواهیم کرد.

صفحه عنوان Super Mario Bros

شروع با اسکریپت Lua

اکنون که شبیه ساز در حال اجرا هستید ، پنجره اسکریپت Lua را از طریق باز کنید Tools -> Lua Consoleبشر در اینجا می توانید اسکریپت ها را باز کنید ، اجرای آنها را کنترل کنید و خروجی آنها را مشاهده کنید. در ویرایشگر متن مورد نظر خود ، پرونده ای به نام Hello.lua ایجاد کنید و خط کد زیر را به آن اضافه کنید:

console.log('Hello World!')
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

با کلیک کردن این کد را اجرا کنید Script -> Open Script در کنسول LUA و پیمایش به جایی که پرونده را ذخیره کرده اید. شما باید “سلام جهان!” چاپ شده در برگه “خروجی”.

برای اکثر اسکریپت های شبیه ساز ، مطلوب است که یک حلقه اجرای اصلی داشته باشید که به طور مداوم اجرا شود تا زمانی که متوقف شود ، اجرای کد قبل از ارائه هر فریم از بازی. دو روش وجود دارد که می توانید این کار را انجام دهید. یکی نوشتن یک حلقه نامحدود با استفاده از emu.frameadvance در پایان هر تکرار ، و دیگری ثبت یک تابع در event.onframestartبشر بیایید نگاهی به هر دوی این موارد بیندازیم.

کد را جایگزین کنید hello.lua با موارد زیر ، که “سلام جهان!” به صفحه برای هر قاب بازی ، به جای کنسول:

while true do
    gui.text(50, 50, 'Hello World!')
    emu.frameadvance()
end
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

می توانید روی اسکریپت خود در کنسول LUA کلیک کنید تا آن را روشن و خاموش کنید و هر بار که دوباره راه اندازی می شوید ، اگر تغییراتی ایجاد کرده اید و پرونده را ذخیره کرده اید ، کد جدید شما بارگیری می شود. این کار را انجام دهید و باید “سلام جهان!” روی صفحه نمایش پاپ کنید و در آنجا بمانید زیرا در هر قاب چاپ می شود. من شخصاً از نوشتن یک حلقه نامحدود که در واقع مفید است ، به عنوان یک عمل توهین آمیز برای همه مواردی که به اشتباه نوشتم ، هیجان زده ام.

سلام جهان در Super Mario Bros نمایش داده شده است

اگر به نظر نمی رسد که حلقه های نامحدود به نظر برسید ، بیایید با استفاده از کاربردی بیشتر این کار را انجام دهیم event.onframestart با کد زیر که همان رفتار را دارد:

با این کار ، بیایید روی کد نوشتن حرکت کنیم تا با خود بازی تعامل داشته باشیم.

خواندن و نوشتن به حافظه

Bizhawk بسیاری از ابزارهای مفید توسعه دهنده را برای به دست آوردن بینش در مورد بازی های بازی شده ارائه می دهد. این برنامه ها هنگام انجام Speedruns یا هک کردن بازی های قدیمی ، مفید هستند. یکی از آنها “ویرایشگر هگز” است ، ابزاری که به شما امکان می دهد رم بازی را در زمان واقعی مشاهده و ویرایش کنید. رم به شکل 4 رقمی (برای بازی های NES) آدرس های شش ضلعی و مقادیر شش ضلعی شش رقمی نمایش داده می شود.

ویرایشگر Hex Bizhawk

ویرایشگر Hex Bizhawk را با کلیک کردن باز کنید Tools -> Hex Editorبشر تمام شماره های دو رقمی Hex که می بینید یک مقدار در RAM بازی در یک مکان خاص را نشان می دهد. می توانید هر مقدار را تغییر دهید تا ببینید چه اتفاقی در بازی می افتد. همچنین می توانید با استفاده از CTRL-F یا کلیک کردن مقادیر خاص را جستجو کنید Edit -> Findبشر به عنوان مثال ، ویرایشگر HEX می تواند برای یافتن بایت های مربوط به تایمر در Super Mario Bros و تغییر زمان به صفر برای کشتن ماریو استفاده شود:

یک GIF هنگام ویرایش مقادیر سحر و جادو تایمر ، ماریو را در حال مرگ است

هوشمندانه ویرایش آدرسهای حافظه می تواند منجر به اصلاحات جالب گیم پلی شود. در این حالت ، ما با ویرایش مقادیر در آدرس های حافظه ، 07F8-07FA به صفر ، ماریو را کشتیم. اما این همچنین می تواند به صورت برنامه ای در یک اسکریپت LUA انجام شود.

یک اسکریپت جدید با کد زیر ایجاد کنید ، که مقادیر تایمر را به کنسول وارد می کند و سپس آنها را به صفر تغییر می دهد تا ماریو را بکشد:

console.log(memory.readbyte(0x07F8) .. memory.readbyte(0x07F9) .. memory.readbyte(0x07FA))

memory.writebyte(0x07F8, 0)
memory.writebyte(0x07F9, 0)
memory.writebyte(0x07FA, 0)
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

در Take ، 0x قبل از یک عدد به این معنی است که شما به یک مقدار شش ضلعی مراجعه می کنید ، و .. یک اپراتور برای هماهنگی رشته است. همانطور که در این کد مشاهده می کنید می توانید مقادیر آدرس Hex را در حافظه بازی با آن بخوانید memory.readbyte و با آنها بنویسید memory.writebyteبشر

ایده حفر کردن در اطراف حافظه بازی ممکن است در ابتدا دلهره آور به نظر برسد ، اما معلوم است که برای بسیاری از بازی ها ، افراد در اینترنت قبلاً این کار را برای شما انجام داده اند! در اینجا یک نقشه قوچ برای برادران Super Mario وجود دارد که سعی کنید با این آدرس ها و ارزش های حافظه برای خود بازی کنید و ببینید چه اتفاقی می افتد.

ورودی کنترل کننده – بازی کردن بازی با کد

گذشته از دستکاری حافظه یک بازی ، می توانید فشار دکمه ای را با JoyPad.set وارد کنید و ورودی را با JoyPad.get بخوانید. نام دکمه ها بسته به هر کنسول بازی ویدیویی متفاوت هستند ، بنابراین در اینجا یک نمودار مفید با مقادیر رشته ای از نام دکمه ها برای هر سیستم وجود دارد.

ورودی های کنترل کننده توسط یک جدول در LUA نشان داده شده است ، که شبیه به یک فرهنگ لغت در پایتون یا یک شی در JavaScript است. به عنوان مثال ، joypad.set({A=true}, 1) دکمه A را روی کنترلر Player 1 فشار می دهد.

با استفاده از ماریو به عنوان نمونه دوباره ، یک اسکریپت LUA را با کد زیر اجرا کنید تا ماریو با سرعت کامل به طور نامحدود به سمت راست حرکت کند:

while true do
    joypad.set({Right=true, B=true}, 1)
    emu.frameadvance()
end
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

یک gif از ماریو که مستقیم در حال اجرا است

این عالی است و در حالی که سرعت کامل در یک Goomba ممکن است خنده دار باشد ، این یک دستور العمل خوب برای موفقیت نیست. چه می شود اگر ما همه چیزهایی را که یاد گرفتیم با نوشتن برخی از کدی هایی که می تواند در کل سطح اول Super Mario Bros اجرا شود ، ترکیب کنیم؟ با اطلاعات موجود در نقشه RAM برای بازی ، می توانیم کد را بنویسیم تا مقادیر از حافظه بازی را بخوانیم تا در مورد زمان پرش تصمیم بگیریم.

فایلی به نام ایجاد کنید level1.lua با کد زیر:

-- A table of indexes of pit locations in the level, as well as
-- two other spots that aren't pits where jumping also helps.
local pits = {[0x1C]=true, [0x24]=true, [0x30]=true, [0x34]=true, [0x3C]=true, [0x54]=true}

while true do
        -- Our button inputs, which will continuously move to the right by default.
        -- Add to this table to jump depending on the situation.
        local input = {Right=true}

        -- Mario's current position
        local mario = memory.readbyte(0x0086)

        -- The first enemy's (on the screen) current position
        local enemy = memory.readbyte(0x0087)

        -- Where we are in the level
        local level_index = memory.readbyte(0x072C)

        -- If Mario is colliding with an object (like a pipe), hold jump for 20 frames.
        if memory.readbyte(0x0490) == 0xFE then
            input.A = true
            for i=1, 20, 1 do
                joypad.set(input, 1)
                emu.frameadvance()
            end

            -- For some reason the above strategy isn't working in these two specific spots.
            if level_index == 0x52 or level_index == 0x60 then
                joypad.set({A=true})
                emu.frameadvance()
            end
        end

        -- If Mario is close to an enemy, jump.
        -- 38 is just a number of pixels that I found works well from experimentation.
        if math.abs(mario - enemy) < 38 then
            input.A = true
        end

        -- If Mario is near a pit, jump.
        if pits[memory.readbyte(0x072C)] then
            -- For some pits, it works better to slow down a bit before jumping.
            -- Advancing a few frames with no button input will do this.
            for i=1, 3, 1 do
                emu.frameadvance()
            end

            input.A = true

            -- Hold A for 20 frames to clear any gaps.
            for i=1, 20, 1 do
                joypad.set(input, 1)
                emu.frameadvance()
            end
        end

        -- Set the button inputs and advance the frame, restarting the loop.
        joypad.set(input, 1)
        emu.frameadvance()
end
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

من برای هر بخش برای توضیح کد ، نظرات خود را ترک کردم ، اما استراتژی اساسی این است که به طور مداوم به سمت راست حرکت کنیم در حالی که در این سناریوها پرش می کنم:

  • هر وقت دشمن خیلی نزدیک شود
  • هنگامی که یک گودال در این نزدیکی وجود دارد
  • اگر ماریو با یک مانع برخورد می کند

پرش هر زمان که دشمن خیلی نزدیک شود ، هر وقت گودال در این نزدیکی وجود دارد ، یا هر زمان که ماریو با یک مانع برخورد می کند. مقادیری در حافظه وجود دارد که نشان دهنده همه این موارد است ، حداقل به اندازه کافی برای این مثال.

بازی را در جهان 1-1 شروع کنید و این کد را اجرا کنید تا ببینید ماریو سطح را کامل می کند! (اگر اجازه دهید این کار را ادامه دهد ، او احتمالاً 1-2 به جهان می رسد و به Goomba دوم باخت).

یک GIF که ماریو را در حال تکمیل سطح از فیلمنامه در حال اجرا نشان می دهد

این پیشرفته ترین یا کارآمدترین روش برای ضرب و شتم سطح نیست ، اما این کار را انجام می دهد. این فقط سطح اول بود ، بنابراین اکنون بقیه به شما بستگی دارند. سعی کنید روی فیلمنامه پیشرفت کنید تا سطح بعدی را شکست دهید!

سطح کامل!

ما در اینجا زمین های مناسبی را در اینجا پوشانده ایم تا شما را با اسکریپت های Lua برای بازی های ویدیویی شروع کنید ، اما Lua API Bizhawk نیز ویژگی های دیگری نیز دارد. با استفاده از ماژول رویداد می توانید توابع را در رویدادها ، مانند ذخیره یا بارگیری حالت های ذخیره یا نوشتن به مکانهای خاص حافظه ثبت کنید. همچنین یک ماژول Comm وجود دارد که برخی از قابلیت های ارتباطی مانند اتصالات سوکت و درخواست های HTTP را ارائه می دهد. اینها همه در مستندات توابع Bizhawk Lua در Tasvideos شرح داده شده است.

من نمی توانم صبر کنم تا ببینم چه نوع بازدیدهای شبیه ساز شما را می سازید. اگر سوالی دارید یا فقط می خواهید هک خود را نشان دهید ، خط را برای من رها کنید.

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا