Mengimplementasikan perilaku blaster adalah proses pemrograman mekanik ledakan dalam pengalaman penembak orang pertama. Sementara pemain dapat melakukan ledakan dengan satu klik atau tekan tombol, menciptakan perilaku ledakan yang memuaskan dan akurat sangat penting karena meningkatkan kesenangan pemain dalam keseluruhan gameplay.
Dengan menggunakan pengalaman laser tag contoh sebagai referensi, bagian dari tutorial ini mengajarkan Anda tentang skrip di balik implementasi perilaku blaster untuk dua jenis blaster yang berbeda, termasuk panduan tentang:
- Mendeteksi kapan pemain menekan tombol ledakan.
- Memeriksa apakah pemain dapat menggunakan blaster mereka jika mereka baru saja menekan tombol ledakan.
- Menghasilkan data ledakan yang memberi tahu server siapa yang memulai ledakan, dari mana asalnya, dan apa tujuan akhir dari setiap sinar laser.
- Memberitahu server tentang data ledakan sehingga dapat melakukan tindakan yang tepat jika ledakan bertubrukan dengan pemain lain.
- Mengatur ulang blaster di antara setiap ledakan untuk memberikan waktu bagi blaster untuk mendingin sebelum dapat melakukan ledakan lagi.
Setelah Anda menyelesaikan bagian ini, Anda akan belajar tentang skrip yang memungkinkan blaster mendeteksi ketika ledakannya bertubrukan dengan pemain lain, lalu mengurangi jumlah kesehatan yang sesuai sesuai dengan setiap jenis blaster.
Mendeteksi input pemain
Langkah pertama untuk mengimplementasikan perilaku blaster adalah mendengarkan kapan pemain menekan tombol ledakan. Jenis input yang digunakan pemain untuk menekan tombol ledakan tergantung pada perangkat yang mereka gunakan untuk mengakses pengalaman tersebut. Misalnya, pengalaman laser tag contoh mendukung kontrol mouse dan keyboard, gamepad, dan kontrol sentuh. Anda dapat melihat setiap jenis input ini di ReplicatedStorage ⟩ UserInputHandler.
Skrip klien ini menggunakan ContextActionService untuk mengikat MouseButton1 dan ButtonR2 ke aksi ledakan. Ini berarti bahwa setiap kali seorang pemain menekan tombol mouse kiri atau tombol R2 pada gamepad, itu memicu sinar laser untuk keluar dari blaster. Perhatikan bahwa HUDGui memiliki tombol untuk ledakan di perangkat mobile, yang akan terhubung nanti dalam skrip.
UserInputHandlerContextActionService:BindAction("_", onBlasterActivated, false,Enum.UserInputType.MouseButton1,Enum.KeyCode.ButtonR2)
Catatan penting lainnya adalah penggunaan Enum.UserInputState.Begin dalam definisi onBlasterActivated(). Banyak interaksi antarmuka pengguna, seperti memilih blaster dalam contoh ini, tidak terjadi sampai setelah tombol mouse dilepaskan (Enum.UserInputState.End), yang memberi pengguna kesempatan terakhir untuk menghindari interaksi tersebut. Namun, mekanik ledakan tidak terasa responsif kecuali terjadi saat tombol ditekan.
Sebagai contoh, Anda dapat mengubah Enum.UserInputState.Begin menjadi Enum.UserInputState.End, lalu melakukan pengujian untuk melihat bagaimana responsivitas ledakan memengaruhi gameplay dari pengalaman tersebut. Misalnya, jika pemain dapat menahan tombol tanpa memicu ledakan, bagaimana hal itu dapat mengubah pengalaman mereka saat menandai pemain lain?
UserInputHandler
local function onBlasterActivated(_actionName: string,
inputState: Enum.UserInputState, _inputObject: InputObject)
if inputState == Enum.UserInputState.End then -- baris yang diperbarui, pastikan untuk mengubah kembali
attemptBlastClient()
end
end
Periksa apakah pemain dapat melakukan ledakan
Setelah UserInputHandler mendeteksi penekanan tombol atau ketukan layar, itu memanggil ReplicatedStorage ⟩ Blaster ⟩ attemptBlastClient untuk memeriksa apakah pemain dapat melakukan ledakan atau tidak. Seperti kebanyakan pemeriksaan dalam pengalaman laser tag contoh, ini terjadi dua kali: pertama di klien, lalu di server. attemptBlastClient kemudian memanggil ReplicatedStorage ⟩ Blaster ⟩ canLocalPlayerBlast untuk melakukan pemeriksaan sederhana dari atribut pemain blasterStateClient:
canLocalPlayerBlast
local function canLocalPlayerBlast(): boolean
return localPlayer:GetAttribute(PlayerAttribute.blasterStateClient) == BlasterState.Ready
end
Jika Anda memeriksa ReplicatedStorage ⟩ Blaster ⟩ BlasterState, Anda dapat melihat bahwa pengalaman ini memiliki tiga status blaster: Ready, Blasting, dan Disabled. Untuk melihat efek dari masing-masing status ini, Anda dapat melakukan pengujian pengalaman, memilih pemain Anda di bawah layanan Players, lalu mengamati atribut blasterStateClient di jendela Properties. Perhatikan bagaimana itu menampilkan Disabled saat Anda memilih blaster Anda, Ready sebagian besar waktu, dan Blasting selama kurang dari satu detik setelah Anda menekan tombol.
Jeda kecil ini mencegah Anda untuk dapat melakukan ledakan secepat Anda mengklik. Misalnya, jika Anda mengubah fungsi untuk selalu mengembalikan true, Anda dapat dengan cepat melakukan ledakan tanpa jeda, yang tidak realistis untuk gameplay laser tag.
canLocalPlayerBlast
local function canLocalPlayerBlast(): boolean
return true -- baris yang diperbarui, pastikan untuk mengubah kembali
end
Menghasilkan data ledakan
Setelah memverifikasi bahwa blaster pemain berada dalam status Ready, attemptBlastClient memanggil ReplicatedStorage ⟩ attemptBlastClient ⟩ blastClient. Langkah pertama yang diambil oleh blastClient adalah mengatur atribut pemain blasterStateClient ke Blasting, yang menghindari kasus tembakan cepat yang sama dari sebelumnya.
Langkah selanjutnya adalah menghasilkan data ledakan. Jika Anda meninjau ReplicatedStorage ⟩ Blaster ⟩ BlastData, Anda dapat melihat bahwa setiap ledakan terdiri dari tiga informasi:
- Pemain yang memulai ledakan.
- Sebuah DataType.CFrame yang mewakili titik asal ledakan.
- Sebuah tabel RayResult yang memuat tujuan akhir dari setiap sinar laser dan pemain yang terkena, jika mengenai pemain lain.
Untuk menghasilkan data ini, blastClient memanggil ReplicatedStorage ⟩ attemptBlastClient ⟩ blastClient ⟩ generateBlastData, yang dapat Anda tinjau di bawah ini.
generateBlastData
local function generateBlastData(): BlastData.Type
local blasterConfig = getBlasterConfig()
local rayDirections = getDirectionsForBlast(
currentCamera.CFrame, blasterConfig)
local rayResults = castLaserRay(
localPlayer, currentCamera.CFrame.Position, rayDirections)
local blastData: BlastData.Type = {
player = localPlayer,
originCFrame = currentCamera.CFrame,
rayResults = rayResults,
}
return blastData
end
Fungsi ini dimulai dengan menggunakan getBlasterConfig untuk mengambil jenis blaster pemain. Contoh ini menyediakan dua jenis blaster: satu yang menghasilkan beberapa sinar dengan sebaran horizontal yang lebar, dan yang lainnya menghasilkan satu sinar tunggal. Anda dapat menemukan konfigurasinya di ReplicatedStorage ⟩ Instances ⟩ LaserBlastersFolder.
Fungsi tersebut kemudian menggunakan currentCamera.CFrame sebagai titik asal untuk ledakan, menyerahkannya ke getDirectionsForBlast. Pada titik ini, kode tidak lagi tentang blaster, tetapi tentang sinar laser, yang akan Anda pelajari lebih lanjut di bagian mendeteksi pukulan dari tutorial. Akhirnya, setelah membuat tabel rayResults, generateBlastData memiliki semua informasi yang dibutuhkan untuk mengembalikan data ledakan ke blastClient.
Memberitahu server
Setelah blastClient memiliki data yang lengkap untuk ledakan, ia memicu dua peristiwa:
blastClientlocal laserBlastedBindableEvent = ReplicatedStorage.Instances.LaserBlastedBindableEventlocal laserBlastedEvent = ReplicatedStorage.Instances.LaserBlastedEventlaserBlastedBindableEvent:Fire(blastData)laserBlastedEvent:FireServer(blastData)
BindableEvent memberi tahu skrip klien lain tentang ledakan. Misalnya, ReplicatedStorage ⟩ FirstPersonBlasterVisuals menggunakan peristiwa ini untuk mengetahui kapan menampilkan efek visual, seperti animasi ledakan dan bilah cooldown. Demikian pula, RemoteEvent memberi tahu skrip server tentang ledakan, yang mulai memproses ledakan di ServerScriptService ⟩ LaserBlastHandler.
LaserBlastHandler
local function onLaserBlastedEvent(playerBlasted: Player, blastData: BlastData.Type)
local validatedBlastData = getValidatedBlastData(playerBlasted, blastData)
if not validatedBlastData then
return
end
if not canPlayerBlast(playerBlasted) then
return
end
blastServer(playerBlasted)
processTaggedPlayers(playerBlasted, blastData)
for _, replicateToPlayer in Players:GetPlayers() do
if playerBlasted == replicateToPlayer then
continue
end
replicateBlastEvent:FireClient(replicateToPlayer, playerBlasted, blastData)
end
end
Untuk membantu mencegah kecurangan, server harus memverifikasi semua data yang dikirim oleh setiap klien. Pemeriksaan ini termasuk:
- Apakah BlastData sebuah tabel? Apakah ia mengandung Class.CFrame dan tabel lain yang dinamai rayResults?
- Apakah pemain memiliki blaster yang dilengkapi?
- Apakah pemain memiliki karakter dan lokasi dalam dunia?
- Setelah mengirim data ledakan, apakah pemain bergerak terlalu jauh dari tempat mereka melakukan ledakan sinar laser?
Pemeriksaan terakhir ini melibatkan penilaian, dan sesuai dengan latensi server dan kecepatan gerakan pemain, Anda mungkin memutuskan bahwa nilai yang berbeda terlalu tinggi untuk pengalaman Anda sendiri. Untuk mendemonstrasikan bagaimana membuat penilaian ini, Anda dapat merasa tentang besarnya perubahan posisi yang tipikal dengan menambahkan pernyataan cetak di getValidatedBlastData dan melakukan pengujian pengalaman.
getValidatedBlastDatalocal distanceFromCharacterToOrigin = blastData.originCFrame.Position - rootPartCFrame.Positionprint(distanceFromCharacterToOrigin.Magnitude) -- baris yang diperbarui, pastikan untuk menghapusif distanceFromCharacterToOrigin.Magnitude > ToleranceValues.DISTANCE_SANITY_CHECK_TOLERANCE_STUDS thenwarn(`Pemain {player.Name} gagal memeriksa keabsahan asal saat melakukan ledakan`)returnend
Saat Anda bergerak dan melakukan ledakan, catat outputnya. Itu mungkin terlihat seperti ini:
1.90196299552917483.15495586395263672.57428836822509774.80445861816406252.6434271335601807
Jika Anda meningkatkan kecepatan gerakan untuk pemain di ReplicatedStorage ⟩ PlayerStateHandler ⟩ togglePlayerMovement, kemudian lakukan pengujian lagi, Anda kemungkinan besar akan mengalami banyak pemeriksaan yang gagal karena gerakan yang berlebihan antara ledakan.
togglePlayerMovementlocal ENABLED_WALK_SPEED = 60 -- baris yang diperbarui, pastikan untuk mengubah kembali
Server kemudian melakukan hal berikut:
- Memvalidasi rayResults.
- Memeriksa apakah pemain dapat melakukan ledakan.
- Mengatur ulang status blaster.
- Mengurangi kesehatan untuk setiap pemain yang terkena.
- Menyebarkan ledakan ke semua pemain lain sehingga mereka dapat melihat visual pihak ketiga.
Untuk informasi lebih lanjut tentang operasi server ini, lihat bagian mendeteksi pukulan dari tutorial.
Mengatur ulang blaster
Dalam pengalaman laser tag contoh, blaster menggunakan mekanik panas. Alih-alih memuat ulang setelah sejumlah ledakan tertentu, mereka memerlukan waktu untuk "mendinginkan" di antara setiap ledakan. Jeda pendinginan yang sama terjadi di klien (blastClient) dan server (blastServer), dengan server bertindak sebagai sumber kebenaran.
blastServer
local blasterConfig = getBlasterConfig(player)
local secondsBetweenBlasts = blasterConfig:GetAttribute("secondsBetweenBlasts")
task.delay(secondsBetweenBlasts, function()
local currentState = player:GetAttribute(PlayerAttribute.blasterStateServer)
if currentState == BlasterState.Blasting then
player:SetAttribute(PlayerAttribute.blasterStateServer, BlasterState.Ready)
end
end)
Atribut secondsBetweenBlasts adalah bagian dari konfigurasi blaster di ReplicatedStorage ⟩ Instances ⟩ LaserBlastersFolder. Setelah jeda secondsBetweenBlasts berlalu, pemain dapat melakukan ledakan lagi, dan seluruh proses diulang. Untuk membantu pemain memahami kapan mereka bisa melakukan ledakan lagi, pengalaman ini menyertakan bilah cooldown.
Pada titik ini, pemain dapat muncul dan respawn, membidik dan melakukan ledakan, tetapi pengalaman ini masih harus menentukan hasil dari setiap ledakan. Di bagian berikutnya dari tutorial, Anda akan belajar bagaimana memprogram kemampuan bagi blaster untuk mendeteksi ketika ledakan mengenai pemain lain, lalu mengurangi jumlah kesehatan pemain yang sesuai sesuai dengan pengaturan blaster.