Dalam tutorial ini, Anda akan belajar cara melemparkan laser dari blaster di Buat alat pemain dan memeriksa apakah atau tidak menyerang pemain.
Raycasting untuk menemukan kolisi
Raycasting membuat sinar tak terlihat dari posisi awal menuju arah tertentu dengan panjang yang ditentukan.Jika sinar bertabrakan dengan objek atau medan di jalurnya, ia akan mengembalikan informasi tentang tabrakan seperti posisi dan objek yang bertabrakan.

Temukan lokasi mouse
Sebelum laser bisa ditembak, Anda harus terlebih dahulu tahu di mana pemain menargetkan.Ini dapat ditemukan dengan raycasting dari lokasi mouse 2D pemain di layar langsung dari kamera ke dunia permainan.Sinar akan bertabrakan dengan apa pun yang ditargetkan pemain dengan mouse.
Buka skrip Kontroler Alat di dalam alat Blaster dari Buat alat pemain.Jika Anda belum menyelesaikan tutorial itu, Anda dapat mengunduh model Blaster dan memasukkannya ke StarterPack.
Di bagian atas skrip, nyatakan konstan bernama MAX_MOUSE_DISTANCE dengan nilai 1000 .
Buat fungsi yang disebut getWorldMousePosition .
local tool = script.Parentlocal MAX_MOUSE_DISTANCE = 1000local function getWorldMousePosition()endlocal function toolEquipped()tool.Handle.Equip:Play()endlocal function toolActivated()tool.Handle.Activate:Play()end-- Hubungkan peristiwa ke fungsi yang tepattool.Equipped:Connect(toolEquipped)tool.Activated:Connect(toolActivated)Gunakan fungsi GetMouseLocation dari UserInputService untuk mendapatkan lokasi mouse 2D pemain di layar.Atribusikan ini ke variabel bernama mouseLocation .
local UserInputService = game:GetService("UserInputService")local tool = script.Parentlocal MAX_MOUSE_DISTANCE = 1000local function getWorldMousePosition()local mouseLocation = UserInputService:GetMouseLocation()end
Sekarang lokasi mouse 2D diketahui, properti X dan Y dapat digunakan sebagai parameter untuk fungsi Camera:ViewportPointToRay() yang membuat Ray dari layar ke dunia game 3D.
Gunakan properti X dan Y dari mouseLocation sebagai argumen untuk fungsi ViewportPointToRay().Ambil ini ke variabel bernama screenToWorldRay .
local function getWorldMousePosition()local mouseLocation = UserInputService:GetMouseLocation()-- Buat sinar dari lokasi mouse 2Dlocal screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)end
Sudah waktunya untuk menggunakan fungsi Raycast untuk memeriksa apakah sinar menyerang objek.Ini membutuhkan posisi awal dan vektor arah: dalam contoh ini, Anda akan menggunakan properti asal dan arah screenToWorldRay .
Panjang vektor arah menentukan seberapa jauh sinar akan melaju.Sinar harus selama MAX_MOUSE_DISTANCE , jadi Anda harus menggandakan vektor arah dengan MAX_MOUSE_DISTANCE .
Deklarasikan variabel bernama directionVector dan atribusikan nilainya dengan screenToWorldRay.Direction dikalikan MAX_MOUSE_DISTANCE .
local function getWorldMousePosition()local mouseLocation = UserInputService:GetMouseLocation()-- Buat sebuah sinar dari lokasi mouse 2Dlocal screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)-- Vektor arah unit dari sinar dikalikan dengan jarak maksimumlocal directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCEPanggil fungsi Raycast dari ruang kerja, menyampaikan properti Asal dari screenToWorldRay sebagai argumen pertama dan directionVector sebagai argumen kedua.Ambil ini ke variabel bernama raycastResult .
local function getWorldMousePosition()local mouseLocation = UserInputService:GetMouseLocation()-- Buat sebuah sinar dari lokasi mouse 2Dlocal screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)-- Vektor arah unit dari sinar dikalikan dengan jarak maksimumlocal directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE-- Raycast dari asal sinar menuju arahnyalocal raycastResult = workspace:Raycast(screenToWorldRay.Origin, directionVector)
Informasi kolisi
Jika operasi raycast menemukan objek yang dipukul oleh sinar, itu akan kembali RaycastResult , yang berisi informasi tentang kolisi antara sinar dan objek.
Properti RaycastResult | Deskripsi |
---|---|
Instansi | Sel BasePart atau Terrain yang dilewati oleh sinar yang bertemu. |
Posisi | Di mana intersepsi terjadi; biasanya titik langsung di permukaan bagian atau medan. |
Bahasa | Material di titik tabrakan. |
Biasa | Vektor normal dari wajah yang tertumpang. Ini dapat digunakan untuk menentukan ke arah mana wajah menunjuk. |
Properti Posisi akan menjadi posisi objek yang ditumpangi mouse.Jika mouse tidak melayang di atas objek mana pun dalam jarak MAX_MOUSE_DISTANCE , raycastResult akan menjadi nil .
Buat pernyataan if untuk memeriksa apakah raycastResult ada.
Jika raycastResult memiliki nilai, return properti Posisi nya.
Jika raycastResult adalah nil maka temukan akhir raycast.Hitung posisi 3D mouse dengan menambahkan screenToWorldRay.Origin dan directionVector bersama-sama.
local function getWorldMousePosition()
local mouseLocation = UserInputService:GetMouseLocation()
-- Buat sebuah sinar dari lokasi mouse 2D
local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
-- Vektor arah unit dari sinar dikalikan dengan jarak maksimum
local directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE
-- Raycast dari asal sinar menuju arahnya
local raycastResult = workspace:Raycast(screenToWorldRay.Origin, directionVector)
if raycastResult then
-- Kembalikan titik intersepsi 3D
return raycastResult.Position
else
-- Tidak ada objek yang dipukul jadi hitung posisinya di akhir ray
return screenToWorldRay.Origin + directionVector
end
end
Tembak ke arah target
Sekarang posisi mouse 3D diketahui, itu dapat digunakan sebagai posisi target untuk menembakkan laser ke arah .Sinar kedua dapat dilemparkan antara senjata pemain dan posisi target menggunakan fungsi Raycast .
Deklarasikan konstan yang disebut MAX_LASER_DISTANCE di bagian atas skrip dan atribusikan ke 500 , atau rentang yang Anda pilih untuk laser blaster.
local UserInputService = game:GetService("UserInputService")local tool = script.Parentlocal MAX_MOUSE_DISTANCE = 1000local MAX_LASER_DISTANCE = 500Buat fungsi yang disebut fireWeapon di bawah fungsi getWorldMousePosition.
Panggil getWorldMousePosition dan atribusikan hasil ke variabel bernama posisi mouse . Ini akan menjadi posisi target untuk raycast.
-- Tidak ada objek yang dipukul jadi hitung posisinya di akhir rayreturn screenToWorldRay.Origin + directionVectorendendlocal function fireWeapon()local mouseLocation = getWorldMousePosition()endlocal function toolEquipped()tool.Handle.Equip:Play()end
Kali ini, vektor arah untuk fungsi raycast akan mewakili arah dari posisi alat pemain ke lokasi target.
Deklarasikan variabel bernama targetDirection dan hitung vektor arah dengan mengurangi posisi alat dari mouseLocation .
Normalisasi vektor dengan menggunakan properti Unit nya. Ini memberinya magnitudo 1, yang memudahkan untuk dikalikan dengan panjang nanti.
local function fireWeapon()local mouseLocation = getWorldMousePosition()-- Hitung vektor arah normalisasi dan kalikan dengan jarak laserlocal targetDirection = (mouseLocation - tool.Handle.Position).UnitendDeklarasikan variabel bernama directionVector dan atribusikan kepadanya targetDirection dikalikan dengan MAX_LASER_DISTANCE.
local targetDirection = (mouseLocation - tool.Handle.Position).Unit-- Arah untuk menembak senjata, dikalikan dengan jarak maksimallocal directionVector = targetDirection * MAX_LASER_DISTANCEend
Sebuah objek RaycastParams dapat digunakan untuk menyimpan parameter tambahan untuk fungsi raycast.Ini akan digunakan dalam laser blaster Anda untuk memastikan raycast tidak secara tidak sengaja bertabrakan dengan pemain yang menembak senjata.Setiap bagian yang termasuk dalam properti dari objek RaycastParams akan diabaikan dalam raycast
Lanjutkan fungsi fireWeapon dan nyatakan variabel yang disebut weaponRaycastParams . Berikan objek RaycastParams baru kepadanya.
Buat tabel yang berisi karakter lokal pemain dan atribusikan ke properti ..
Raycast dari posisi handle alat pemain, ke arah menuju directionVector.Ingatlah untuk menambahkan weaponRaycastParams sebagai argumen kali ini.Ambil ini ke variabel bernama weaponRaycastResult .
local UserInputService = game:GetService("UserInputService")
local Players = game:GetService("Players")
local tool = script.Parent
local MAX_MOUSE_DISTANCE = 1000
local MAX_LASER_DISTANCE = 500
local function getWorldMousePosition()
local function fireWeapon()
local mouseLocation = getWorldMousePosition()
-- Hitung vektor arah normalisasi dan kalikan dengan jarak laser
local targetDirection = (mouseLocation - tool.Handle.Position).Unit
-- Arah untuk menembak senjata dikalikan dengan jarak maksimal
local directionVector = targetDirection * MAX_LASER_DISTANCE
-- Abaikan karakter pemain untuk mencegah mereka melukai diri sendiri
local weaponRaycastParams = RaycastParams.new()
weaponRaycastParams.FilterDescendantsInstances = {Players.LocalPlayer.Character}
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)
end
Akhirnya, Anda perlu memeriksa bahwa operasi raycast kembali nilai.Jika nilai dikembalikan, objek terkena oleh sinar dan laser dapat dibuat di antara senjata dan lokasi pukul.Jika tidak ada yang dikembalikan, posisi akhir perlu dihitung untuk membuat laser.
Deklarasikan variabel kosong bernama hitPosition .
Gunakan pernyataan jika untuk memeriksa apakah weaponRaycastResult memiliki nilai. Jika objek dipukul, atribusikan weaponRaycastResult.Position ke hitPosition .
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)-- Periksa apakah ada objek yang terkena antara posisi awal dan akhirlocal hitPositionif weaponRaycastResult thenhitPosition = weaponRaycastResult.PositionendJika weaponRaycastResult tidak memiliki nilai, hitung posisi akhir raycast dengan menambahkan bersama-sama posisi **** dari handle alat dengan directionVector.Ambil ini ke hitPosition .
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)-- Periksa apakah ada objek yang terkena antara posisi awal dan akhirlocal hitPositionif weaponRaycastResult thenhitPosition = weaponRaycastResult.Positionelse-- Hitung posisi akhir berdasarkan jarak laser maksimumhitPosition = tool.Handle.Position + directionVectorendendNavigasikan ke fungsi toolActivated dan panggil fungsi fireWeapon sehingga laser menembak setiap kali alat diaktifkan.
local function toolActivated()tool.Handle.Activate:Play()fireWeapon()end
Periksa objek yang dipukul
Untuk menemukan apakah objek yang dipukul oleh laser adalah bagian dari karakter pemain atau hanya sepotong pemandangan, Anda perlu mencari Humanoid, karena setiap karakter memiliki satu.
Pertama, Anda perlu menemukan model karakter **** .Jika bagian dari karakter terkena, Anda tidak dapat mengasumsikan bahwa orangtua objek yang dipukul adalah karakter.Laser bisa telah memukul bagian tubuh, aksesori, atau alat, semua yang terletak di berbagai bagian hierarki karakter.
Anda dapat menggunakan FindFirstAncestorOfClass untuk menemukan leluhur model karakter dari objek yang dipukul oleh laser, jika ada.Jika Anda menemukan model dan memiliki manusia, dalam kebanyakan kasus Anda dapat mengasumsikan itu adalah karakter.
Tambahkan kode yang disorot di bawah ke pernyataan weaponRaycastResult jika untuk memeriksa apakah karakter terkena.
-- Periksa apakah ada objek yang terkena antara posisi awal dan akhirlocal hitPositionif weaponRaycastResult thenhitPosition = weaponRaycastResult.Position-- Hit instansi akan menjadi anak dari model karakter-- Jika humanoid ditemukan di model maka kemungkinan karakter pemainlocal characterModel = weaponRaycastResult.Instance:FindFirstAncestorOfClass("Model")if characterModel thenlocal humanoid = characterModel:FindFirstChildWhichIsA("Humanoid")if humanoid thenprint("Player hit")endendelse-- Hitung posisi akhir berdasarkan jarak laser maksimumhitPosition = tool.Handle.Position + directionVectorend
Sekarang laser blaster harus mencetak Player hit ke jendela Output setiap kali operasi raycast menyerang pemain lain.
Tes dengan banyak pemain
Dua pemain diperlukan untuk menguji apakah raycast senjata menemukan pemain lain, jadi Anda perlu memulai server lokal.
Pilih tab Tes di Studio.
Pastikan dropdown pemain diatur ke '2 Pemain' dan klik tombol Mulai untuk memulai server lokal dengan 2 klien.Tiga jendela akan muncul.Jendela pertama akan menjadi server lokal, jendela lainnya akan menjadi klien untuk Player1 dan Player2.
Pada satu klien, uji penembakan pemain lain dengan senjata dengan mengkliknya.“Pemain terkena” harus ditampilkan dalam output setiap kali pemain ditembak.
Anda dapat mengetahui lebih lanjut tentang tab Tes di sini .
Temukan posisi laser
Peledak harus menembakkan sinar merah ke targetnya.Fungsi untuk ini akan berada di dalam ModuleScript sehingga dapat digunakan kembali di skrip lain nanti.Pertama, skrip perlu menemukan posisi di mana beam laser harus ditampilkan.
Buat ModuleScript bernama LaserRenderer yang berada di bawah StarterPlayerScripts di bawah StarterPlayer.
Buka skrip dan ganti nama tabel modul ke nama skrip LaserRenderer .
Deklarasikan variabel bernama SHOT_DURATION dengan nilai 0,15 .Ini akan menjadi jumlah waktu (dalam detik) laser terlihat untuk.
Buat fungsi LaserRenderer bernama createLaser dengan dua parameter yang disebut toolHandle dan endPosition .
local LaserRenderer = {}local SHOT_DURATION = 0.15 -- Waktu ketika laser terlihat untuk-- Buat balok laser dari posisi awal menuju posisi akhirfunction LaserRenderer.createLaser(toolHandle, endPosition)endreturn LaserRendererDeklarasikan variabel bernama startPosition dan atur properti Posisi dari toolHandle sebagai nilainya.Ini akan menjadi posisi laser blaster pemain.
Deklarasikan variabel bernama jarak laser dan kurangi endPosition dari startPosition untuk menemukan perbedaan antara dua vektor.Gunakan properti Magnitude dari ini untuk mendapatkan panjang laser.
function LaserRenderer.createLaser(toolHandle, endPosition)local startPosition = toolHandle.Positionlocal laserDistance = (startPosition - endPosition).MagnitudeendDeklarasikan variabel lasercFrame untuk menyimpan posisi dan orientasi balok laser.Posisi harus menjadi titik tengah dari awal dan akhir balok.Gunakan CFrame.lookAt untuk membuat baru CFrame terletak di startPosition dan menghadap ke endPosition .Gandakan ini dengan CFrame baru dengan nilai sumbu Z seperti setengah negatif laserDistance untuk mendapatkan titik tengah.
function LaserRenderer.createLaser(toolHandle, endPosition)local startPosition = toolHandle.Positionlocal laserDistance = (startPosition - endPosition).Magnitudelocal laserCFrame = CFrame.lookAt(startPosition, endPosition) * CFrame.new(0, 0, -laserDistance / 2)end
Buat bagian laser
Sekarang Anda tahu di mana membuat balok laser, Anda perlu menambahkan balok itu sendiri. Ini dapat dilakukan dengan mudah dengan bagian Neon.
Deklarasikan variabel laserPart dan atribusikan kepadanya instansi baru Part.
Tetapkan properti berikut dari laserPart :
- Ukuran : Vector3.new(0.2, 0.2, jarak laser)
- CFrame : laserCFrame
- Terikat : benar
- Dapat Terjadi Tabrakan : false
- Warna : Color3.fromRGB(225, 0, 0) (warna merah yang kuat)
- Bahan : Enum.Material.Neon
Orangtua laserPart ke Ruang kerja .
Tambahkan bagian ke layanan Debris sehingga dihapus setelah jumlah detik dalam variabel SHOT_DURATION.
function LaserRenderer.createLaser(toolHandle, endPosition)local startPosition = toolHandle.Positionlocal laserDistance = (startPosition - endPosition).Magnitudelocal laserCFrame = CFrame.lookAt(startPosition, endPosition) * CFrame.new(0, 0, -laserDistance / 2)local laserPart = Instance.new("Part")laserPart.Size = Vector3.new(0.2, 0.2, laserDistance)laserPart.CFrame = laserCFramelaserPart.Anchored = truelaserPart.CanCollide = falselaserPart.Color = Color3.fromRGB(225, 0, 0)laserPart.Material = Enum.Material.NeonlaserPart.Parent = workspace-- Tambahkan balok laser ke layanan Debris untuk dihapus dan dibersihkanDebris:AddItem(laserPart, SHOT_DURATION)end
Sekarang fungsi untuk menampilkan balok laser selesai, dapat dipanggil oleh Kontroler Alat .
Di bagian atas skrip Kontroler Alat , nyatakan variabel bernama LaserRenderer dan memerlukan Modul Skrip Laserplier yang terletak di PlayerScripts.
local UserInputService = game:GetService("UserInputService")local Players = game:GetService("Players")local LaserRenderer = require(Players.LocalPlayer.PlayerScripts.LaserRenderer)local tool = script.ParentDi bagian bawah fungsi fireWeapon , panggil fungsi LaserRenderer createLaser menggunakan handle alat dan hitPosition sebagai argumen.
-- Hitung posisi akhir berdasarkan jarak laser maksimumhitPosition = tool.Handle.Position + directionVectorendLaserRenderer.createLaser(tool.Handle, hitPosition)endUji senjata dengan mengklik tombol Mainkan. Sebuah balok laser harus terlihat antara senjata dan mouse saat alat diaktifkan.
Kontrol beri ratingkebakaran senjata
Senjata membutuhkan penundaan antara setiap tembakan untuk menghentikan pemain menangani terlalu banyak kerusakan dalam waktu singkat.Ini dapat dikendalikan dengan memeriksa apakah cukup waktu telah berlalu sejak pemain terakhir ditembak.
Deklarasikan variabel di bagian atas ToolController yang disebut FIRE_RATE .Ini akan menjadi waktu minimum antara setiap tembakan.Berikan nilai pilihan Anda; contoh ini menggunakan 0,3 detik.
Deklarasikan variabel lain di bawahnya yang disebut waktu Tembakan Sebelumnya dengan nilai 0 .Ini menyimpan terakhir kali pemain menembak dan akan diperbarui dengan setiap tembakan.
local MAX_MOUSE_DISTANCE = 1000local MAX_LASER_DISTANCE = 300local FIRE_RATE = 0.3local timeOfPreviousShot = 0Buat fungsi yang disebut bisaMenembakSenjata tanpa parameter.Fungsi ini akan melihat berapa banyak waktu telah berlalu sejak tembakan sebelumnya dan kembalikan benar atau salah.
local FIRE_RATE = 0.3local timeOfPreviousShot = 0-- Periksa apakah cukup waktu telah berlalu sejak tembakan sebelumnya ditembaklocal function canShootWeapon()endlocal function getWorldMousePosition()Di dalam fungsi menyatakan variabel bernama waktu saat ini ; atribusikan kepadanya hasil memanggil fungsi tick().Ini mengembalikan berapa banyak waktu telah berlalu, dalam detik, sejak 1 Januari 1970 (tanggal acak yang banyak digunakan untuk menghitung waktu).
Kurangi timeOfPreviousShot dari currentTime dan kembalikan false jika hasilnya lebih kecil dari FIRE_RATE ; jika tidak, kembalikan true .
-- Periksa apakah cukup waktu telah berlalu sejak tembakan sebelumnya ditembaklocal function canShootWeapon()local currentTime = tick()if currentTime - timeOfPreviousShot < FIRE_RATE thenreturn falseendreturn trueendPada akhir fungsi fireWeapon , perbarui timeOfPreviousShot setiap kali senjata ditembak menggunakan tick .
hitPosition = tool.Handle.Position + directionVectorendtimeOfPreviousShot = tick()LaserRenderer.createLaser(tool.Handle, hitPosition)endDi dalam fungsi toolActivated, buat pernyataan jika dan panggil canShootWeapon untuk memeriksa apakah senjata dapat ditembak.
local function toolActivated()if canShootWeapon() thentool.Handle.Activate:Play()fireWeapon()endend
Ketika Anda menguji blaster, Anda harus menemukan bahwa tidak peduli seberapa cepat Anda mengklik, akan selalu ada jeda singkat 0,3 detik di antara setiap tembakan.
Merusak pemain
Klien tidak dapat merusak klien lain secara langsung; server perlu bertanggung jawab untuk mengeluarkan kerusakan saat pemain terkena.
Klien dapat menggunakan RemoteEvent untuk memberi tahu server bahwa karakter telah dipukul.Ini harus disimpan di ReplicatedStorage , di mana mereka terlihat oleh klien dan server.
Buat Folder di ReplicatedStorage bernama Peristiwa .
Masukkan Acara Jarak Jauh ke dalam folder Acara dan beri namanya Karakter Kerusakan .
Di ToolController , buat variabel pada awal skrip untuk ReplicatedStorage dan folder Acara.
local UserInputService = game:GetService("UserInputService")local Players = game:GetService("Players")local ReplicatedStorage = game:GetService("ReplicatedStorage")local LaserRenderer = require(Players.LocalPlayer.PlayerScripts.LaserRenderer)local tool = script.Parentlocal eventsFolder = ReplicatedStorage.Eventslocal MAX_MOUSE_DISTANCE = 1000local MAX_LASER_DISTANCE = 500Ganti pernyataan cetak dengan baris Luau untuk menembakkan peristiwa jarak jauh DamageCharacter dengan variabel sebagai argumen.
local characterModel = weaponRaycastResult.Instance:FindFirstAncestorOfClass("Model")if characterModel thenlocal humanoid = characterModel:FindFirstChildWhichIsA("Humanoid")if humanoid theneventsFolder.DamageCharacter:FireServer(characterModel)endendelse-- Hitung posisi akhir berdasarkan jarak laser maksimumhitPosition = tool.Handle.Position + directionVectorend
Server perlu memberikan kerusakan kepada pemain yang telah terkena saat acara ditembak.
Sisipkan Skrip ke ServerScriptService dan beri namanya ServerLaserManager .
Deklarasikan variabel bernama LASER_DAMAGE dan atur ke 10 , atau nilai pilihan Anda.
Buat fungsi bernama damageCharacter dengan dua parameter yang disebut playerFired dan characterToDamage .
Di dalam fungsi, temukan Humanoid karakter dan kurangi LASER_DAMAGE dari kesehatannya.
Hubungkan fungsi damageCharacter ke acara remote Karakter Kerusakan di folder Acara.
local ReplicatedStorage = game:GetService("ReplicatedStorage")local eventsFolder = ReplicatedStorage.Eventslocal LASER_DAMAGE = 10function damageCharacter(playerFired, characterToDamage)local humanoid = characterToDamage:FindFirstChildWhichIsA("Humanoid")if humanoid then-- Hapus kesehatan dari karakterhumanoid.Health -= LASER_DAMAGEendend-- Hubungkan peristiwa ke fungsi yang tepateventsFolder.DamageCharacter.OnServerEvent:Connect(damageCharacter)Uji blaster dengan 2 pemain dengan memulai server lokal.Ketika Anda menembak pemain lain, kesehatan mereka akan berkurang dengan nomor yang ditugaskan ke LASER_DAMAGE.
Render balok laser pemain lain
Saat ini, sinar laser dibuat oleh klien yang menembak senjata, jadi hanya mereka yang akan dapat melihat sinar laser.
Jika balok laser dibuat di server maka semua orang akan dapat melihatnya.Namun, akan ada penundaan kecil antara klien menembak senjata dan server yang menerima informasi tentang tembakan .Ini berarti klien menembak senjata akan melihat penundaan antara ketika mereka mengaktifkan senjata dan ketika mereka melihat balok laser; senjata akan terasa lambat sebagai hasilnya.
Untuk memecahkan masalah ini, setiap klien akan membuat beam laser sendiri.Ini berarti klien menembak senjata akan melihat balok laser secara langsung.Klien lain akan mengalami penundaan kecil antara ketika pemain lain menembak dan sebuah balok muncul.Ini adalah skenario kasus terbaik: tidak ada cara untuk berkomunikasi dengan laser klien ke klien lain lebih cepat.
Klien penembak
Pertama, klien perlu memberi tahu server bahwa ia telah menembakkan laser dan memberikan posisi akhir.
Masukkan Acara Jarak Jauh ke dalam folder Acara di ReplicatedStorage dan beri namanya LaserFired .
Temukan fungsi fireWeapon di skrip Kontroler Alat .Pada akhir fungsi, nyalakan acara remote LaserFired menggunakan hitPosition sebagai argumen.
hitPosition = tool.Handle.Position + directionVectorendtimeOfPreviousShot = tick()eventsFolder.LaserFired:FireServer(hitPosition)LaserRenderer.createLaser(tool.Handle, hitPosition)end
Penyedia server
Server sekarang harus menerima peristiwa yang klien telah menembak dan memberi tahu semua klien posisi awal dan akhir laser sehingga mereka juga dapat menyajikannya.
Dalam skrip ServerLaserManager , buat fungsi bernama playerFiredLaser di atas damageCharacter dengan dua parameter yang disebut playerFired dan endPosition.
Hubungkan fungsi ke acara remote LaserFired .
-- Beritahu semua klien bahwa laser telah ditembak sehingga mereka dapat menampilkan laserlocal function playerFiredLaser(playerFired, endPosition)end-- Hubungkan peristiwa ke fungsi yang tepateventsFolder.DamageCharacter.OnServerEvent:Connect(damageCharacter)eventsFolder.LaserFired.OnServerEvent:Connect(playerFiredLaser)
Server membutuhkan posisi awal laser.Ini bisa dikirim dari klien, tetapi sebaiknya menghindari mempercayai klien di mana mungkin.Posisi pegangan senjata karakter akan menjadi posisi awal, sehingga server dapat menemukannya dari sana.
Buat fungsi getPlayerToolHandle di atas fungsi playerFiredLaser dengan parameter yang disebut player .
Gunakan kode berikut untuk mencari karakter pemain untuk senjata dan kembalikan objek handle.
local LASER_DAMAGE = 10-- Temukan handle alat yang ditahan pemainlocal function getPlayerToolHandle(player)local weapon = player.Character:FindFirstChildOfClass("Tool")if weapon thenreturn weapon:FindFirstChild("Handle")endend-- Beritahu semua klien bahwa laser telah ditembak sehingga mereka dapat menampilkan laserlocal function playerFiredLaser(playerFired, endPosition)
Server sekarang dapat memanggil FireAllClients pada acara remote LaserFired untuk mengirimkan informasi yang diperlukan untuk menampilkan laser kepada klien.Ini termasuk pemain yang menembak laser (jadi klien untuk pemain itu tidak menampilkan laser dua kali), tangan dari blaster (yang bertindak sebagai posisi awal untuk laser) dan posisi akhir laser.
Dalam fungsi playerFiredLaser , panggil fungsi getPlayerToolHandle dengan playerFired sebagai argumen dan atribusikan nilai ke variabel bernama toolHandle .
Jika toolHandle ada, nyalakan acara LaserFired untuk semua klien menggunakan playerFired , toolHandle dan endPosition sebagai argumen.
-- Beritahu semua klien bahwa laser telah ditembak sehingga mereka dapat menampilkan laserlocal function playerFiredLaser(playerFired, endPosition)local toolHandle = getPlayerToolHandle(playerFired)if toolHandle theneventsFolder.LaserFired:FireAllClients(playerFired, toolHandle, endPosition)endend
Render pada klien
Sekarang FireAllClients telah dipanggil, masing-masing klien akan menerima peristiwa dari server untuk menampilkan balok laser.Setiap klien dapat menggunakan kembali modul LaserRenderer sebelumnya untuk menampilkan balok laser menggunakan posisi pegangan dan nilai posisi akhir alat yang dikirim oleh server.Pemain yang menembakkan balok laser di tempat pertama harus mengabaikan peristiwa ini jika tidak mereka akan melihat 2 laser.
Buat Skrip Lokal di StarterPlayerScripts yang disebut Manajer Laser Klien .
Di dalam skrip, diperlukan modul LaserRenderer .
Buat fungsi bernama createPlayerLaser dengan parameter playerWhoShot , toolHandle dan endPosition .
Hubungkan fungsi ke acara remote LaserFired di folder Acara.
Dalam fungsi, gunakan pernyataan jika untuk memeriksa apakah playerWhoShot melakukan tidak sama dengan LocalPlayer.
Di dalam pernyataan if, panggil fungsi createLaser dari modul LaserRenderer menggunakan toolHandle dan endPosition sebagai argumen.
local Players = game:GetService("Players")local ReplicatedStorage = game:GetService("ReplicatedStorage")local LaserRenderer = require(script.Parent:WaitForChild("LaserRenderer"))local eventsFolder = ReplicatedStorage.Events-- Tampilkan laser pemain lainlocal function createPlayerLaser(playerWhoShot, toolHandle, endPosition)if playerWhoShot ~= Players.LocalPlayer thenLaserRenderer.createLaser(toolHandle, endPosition)endendeventsFolder.LaserFired.OnClientEvent:Connect(createPlayerLaser)Uji blaster dengan 2 pemain dengan memulai server lokal.Posisikan setiap klien di berbagai sisi monitor Anda sehingga Anda dapat melihat kedua jendela sekaligus.Ketika Anda menembak pada satu klien, Anda harus melihat laser pada klien lain.
Efek suara
Efek suara penembakan saat ini hanya dimainkan di klien yang menembakkan proyektil.Anda perlu memindahkan kode untuk memutar suara sehingga pemain lain juga akan mendengarnya.
Dalam skrip Kontroler Alat , navigasikan ke fungsi toolActivated dan hapus baris yang memainkan suara Aktifkan.
local function toolActivated()if canShootWeapon() thenfireWeapon()endendDi bagian bawah fungsi createLaser di LaserRenderer , nyatakan variabel bernama shootingSound dan gunakan metode FindFirstChild() dari toolHandle untuk memeriksa suara Aktifkan .
Gunakan pernyataan jika untuk memeriksa apakah shootingSound ada; jika ada, panggil fungsi Bermain nya.
laserPart.Parent = workspace-- Tambahkan balok laser ke layanan Debris untuk dihapus dan dibersihkanDebris:AddItem(laserPart, SHOT_DURATION)-- Mainkan suara tembak senjatalocal shootingSound = toolHandle:FindFirstChild("Activate")if shootingSound thenshootingSound:Play()endend
Aman jarak jauh menggunakan validasi
Jika server tidak memeriksa data dari permintaan masuk, seorang peretas dapat menyalahgunakan fungsi dan acara remote dan menggunakannya untuk mengirim nilai palsu ke server.Penting untuk menggunakan validasi sisi server untuk mencegah ini.
Dalam bentuknya saat ini, acara remote Karakter Kerusakan sangat rentan terhadap serangan.Peretas bisa menggunakan acara ini untuk merusak pemain mana pun yang mereka inginkan dalam permainan tanpa menembak mereka.
Validasi adalah proses memeriksa bahwa nilai yang dikirim ke server realistis. Dalam kasus ini, server perlu:
- Periksa apakah jarak antara pemain dan posisi yang dipukul oleh laser berada dalam batas tertentu.
- Raycast antara senjata yang menembak laser dan posisi pukul untuk memastikan tembakan itu mungkin dan tidak melalui dinding apa pun.
Pelanggan
Klien perlu mengirimkan server posisi yang dipukul oleh raycast sehingga dapat memeriksa apakah jaraknya realistis.
Di ToolController , navigasikan ke baris di mana peristiwa remote DamageCharacter ditembak di fungsi fireWeapon.
Tambahkan hitPosition sebagai argumen.
if characterModel thenlocal humanoid = characterModel:FindFirstChildWhichIsA("Humanoid")if humanoid theneventsFolder.DamageCharacter:FireServer(characterModel, hitPosition)endend
Pelayan
Klien sekarang mengirim parameter tambahan melalui acara remote DamageCharacter, jadi ServerLaserManager perlu disesuaikan untuk menerimanya.
Dalam skrip ServerLaserManager , tambahkan parameter hitPosition ke fungsi damageCharacter.
function damageCharacter(playerFired, characterToDamage, hitPosition)local humanoid = characterToDamage:FindFirstChildWhichIsA("Humanoid")if humanoid then-- Hapus kesehatan dari karakterhumanoid.Health -= LASER_DAMAGEendendDi bawah fungsi getPlayerToolHandle , buat fungsi bernama isHitValid dengan tiga parameter: playerFired , characterToDamage dan hitPosition .
endlocal function isHitValid(playerFired, characterToDamage, hitPosition)end
Pemeriksaan pertama akan menjadi jarak antara posisi pukul dan karakter yang dipukul.
Deklarasikan variabel bernama MAX_HIT_PROXIMITY di bagian atas skrip dan atribusikan nilai 10 kepadanya.Ini akan menjadi jarak maksimum yang diizinkan antara hit dan karakter.Toleransi diperlukan karena karakter mungkin bergerak sedikit sejak klien menembakkan peristiwa.
local ReplicatedStorage = game:GetService("ReplicatedStorage")local eventsFolder = ReplicatedStorage.Eventslocal LASER_DAMAGE = 10local MAX_HIT_PROXIMITY = 10Dalam fungsi isHitValid, hitung jarak antara karakter dan posisi pukul.Jika jarak lebih besar dari MAX_HIT_PROXIMITY maka return false .
local function isHitValid(playerFired, characterToDamage, hitPosition)-- Verifikasi jarak antara karakter yang dipukul dan posisi pukulanlocal characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitudeif characterHitProximity > MAX_HIT_PROXIMITY thenreturn falseendend
Pemeriksaan kedua akan melibatkan raycast antara senjata yang ditembak dan posisi pukul.Jika raycast kembali objek yang bukan karakter, Anda dapat mengasumsikan tembakan tidak valid karena sesuatu menghalangi tembakan.
Salin kode di bawah ini untuk melakukan periksaini. Kembalikan benar pada akhir fungsi: jika mencapai akhiri, semua pemeriksaan telah lulus.
local function isHitValid(playerFired, characterToDamage, hitPosition)-- Verifikasi jarak antara karakter yang dipukul dan posisi pukulanlocal characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitudeif characterHitProximity > 10 thenreturn falseend-- Periksa apakah menembak melalui dindinglocal toolHandle = getPlayerToolHandle(playerFired)if toolHandle thenlocal rayLength = (hitPosition - toolHandle.Position).Magnitudelocal rayDirection = (hitPosition - toolHandle.Position).Unitlocal raycastParams = RaycastParams.new()raycastParams.FilterDescendantsInstances = {playerFired.Character}local rayResult = workspace:Raycast(toolHandle.Position, rayDirection * rayLength, raycastParams)-- Jika instansi terkena yang bukan karakter maka abaikan tembakan ituif rayResult and not rayResult.Instance:IsDescendantOf(characterToDamage) thenreturn falseendendreturn trueendDeklarasikan variabel dalam fungsi damageCharacter yang disebut validShot .Ambil hasil panggilan ke fungsi isHitValid dengan tiga argumen: playerFired , characterToDamage dan hitPosition .
Dalam pernyataan di bawah ini, tambahkan operator dan untuk memeriksa apakah validShot adalah benar .
function damageCharacter(playerFired, characterToDamage, hitPosition)local humanoid = characterToDamage:FindFirstChildWhichIsA("Humanoid")local validShot = isHitValid(playerFired, characterToDamage, hitPosition)if humanoid and validShot then-- Hapus kesehatan dari karakterhumanoid.Health -= LASER_DAMAGEendend
Sekarang acara remote karakter kerusakan lebih aman dan akan mencegah sebagian besar pemain menyalahgunakannya.Perhatikan bahwa beberapa pemain jahat sering menemukan cara di sekitar validasi; menjaga acara jarak jauh aman adalah upaya terus menerus.
Laser blaster Anda sekarang selesai, dengan sistem deteksi serangan dasar menggunakan raycasting.Coba tutorial Mendeteksi Input Pengguna untuk mengetahui bagaimana Anda dapat menambahkan tindakan pengisian ulang ke laser blaster Anda, atau membuat peta permainan menyenangkan dan mencoba laser blaster Anda dengan pemain lain!
Kode kode
Kontroler Peralatan
local UserInputService = game:GetService("UserInputService")
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local LaserRenderer = require(Players.LocalPlayer.PlayerScripts.LaserRenderer)
local tool = script.Parent
local eventsFolder = ReplicatedStorage.Events
local MAX_MOUSE_DISTANCE = 1000
local MAX_LASER_DISTANCE = 500
local FIRE_RATE = 0.3
local timeOfPreviousShot = 0
-- Periksa apakah cukup waktu telah berlalu sejak tembakan sebelumnya ditembak
local function canShootWeapon()
local currentTime = tick()
if currentTime - timeOfPreviousShot < FIRE_RATE then
return false
end
return true
end
local function getWorldMousePosition()
local mouseLocation = UserInputService:GetMouseLocation()
-- Buat sinar dari lokasi mouse 2D
local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
-- Vektor arah unit dari sinar dikalikan dengan jarak maksimum
local directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE
-- Raycast dari asal roy ke arahnya
local raycastResult = workspace:Raycast(screenToWorldRay.Origin, directionVector)
if raycastResult then
-- Kembalikan titik intersepsi 3D
return raycastResult.Position
else
-- Tidak ada objek yang dipukul jadi hitung posisinya di akhir ray
return screenToWorldRay.Origin + directionVector
end
end
local function fireWeapon()
local mouseLocation = getWorldMousePosition()
-- Hitung vektor arah normalisasi dan kalikan dengan jarak laser
local targetDirection = (mouseLocation - tool.Handle.Position).Unit
-- Arah untuk menembak senjata, dikalikan dengan jarak maksimal
local directionVector = targetDirection * MAX_LASER_DISTANCE
-- Abaikan karakter pemain untuk mencegah mereka melukai diri sendiri
local weaponRaycastParams = RaycastParams.new()
weaponRaycastParams.FilterDescendantsInstances = {Players.LocalPlayer.Character}
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)
-- Periksa apakah ada objek yang terkena antara posisi awal dan akhir
local hitPosition
if weaponRaycastResult then
hitPosition = weaponRaycastResult.Position
-- Hit instansi akan menjadi anak dari model karakter
-- Jika humanoid ditemukan di model maka kemungkinan karakter pemain
local characterModel = weaponRaycastResult.Instance:FindFirstAncestorOfClass("Model")
if characterModel then
local humanoid = characterModel:FindFirstChildWhichIsA("Humanoid")
if humanoid then
eventsFolder.DamageCharacter:FireServer(characterModel, hitPosition)
end
end
else
-- Hitung posisi akhir berdasarkan jarak laser maksimum
hitPosition = tool.Handle.Position + directionVector
end
timeOfPreviousShot = tick()
eventsFolder.LaserFired:FireServer(hitPosition)
LaserRenderer.createLaser(tool.Handle, hitPosition)
end
local function toolEquipped()
tool.Handle.Equip:Play()
end
local function toolActivated()
if canShootWeapon() then
fireWeapon()
end
end
tool.Equipped:Connect(toolEquipped)
tool.Activated:Connect(toolActivated)
Penyaji Laser
local LaserRenderer = {}
local Debris = game:GetService("Debris")
local SHOT_DURATION = 0.15 -- Waktu ketika laser terlihat untuk
-- Buat balok laser dari posisi awal menuju posisi akhir
function LaserRenderer.createLaser(toolHandle, endPosition)
local startPosition = toolHandle.Position
local laserDistance = (startPosition - endPosition).Magnitude
local laserCFrame = CFrame.lookAt(startPosition, endPosition) * CFrame.new(0, 0, -laserDistance / 2)
local laserPart = Instance.new("Part")
laserPart.Size = Vector3.new(0.2, 0.2, laserDistance)
laserPart.CFrame = laserCFrame
laserPart.Anchored = true
laserPart.CanCollide = false
laserPart.Color = Color3.fromRGB(255, 0, 0)
laserPart.Material = Enum.Material.Neon
laserPart.Parent = workspace
-- Tambahkan balok laser ke layanan Debris untuk dihapus dan dibersihkan
Debris:AddItem(laserPart, SHOT_DURATION)
-- Mainkan suara tembak senjata
local shootingSound = toolHandle:FindFirstChild("Activate")
if shootingSound then
shootingSound:Play()
end
end
return LaserRenderer
Manajer Laser Server
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local eventsFolder = ReplicatedStorage.Events
local LASER_DAMAGE = 10
local MAX_HIT_PROXIMITY = 10
-- Temukan handle alat yang ditahan pemain
local function getPlayerToolHandle(player)
local weapon = player.Character:FindFirstChildOfClass("Tool")
if weapon then
return weapon:FindFirstChild("Handle")
end
end
local function isHitValid(playerFired, characterToDamage, hitPosition)
-- Verifikasi jarak antara karakter yang dipukul dan posisi pukulan
local characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitude
if characterHitProximity > MAX_HIT_PROXIMITY then
return false
end
-- Periksa apakah menembak melalui dinding
local toolHandle = getPlayerToolHandle(playerFired)
if toolHandle then
local rayLength = (hitPosition - toolHandle.Position).Magnitude
local rayDirection = (hitPosition - toolHandle.Position).Unit
local raycastParams = RaycastParams.new()
raycastParams.FilterDescendantsInstances = {playerFired.Character}
local rayResult = workspace:Raycast(toolHandle.Position, rayDirection * rayLength, raycastParams)
-- Jika instansi terkena yang bukan karakter maka abaikan tembakan itu
if rayResult and not rayResult.Instance:IsDescendantOf(characterToDamage) then
return false
end
end
return true
end
-- Beritahu semua klien bahwa laser telah ditembak sehingga mereka dapat menampilkan laser
local function playerFiredLaser(playerFired, endPosition)
local toolHandle = getPlayerToolHandle(playerFired)
if toolHandle then
eventsFolder.LaserFired:FireAllClients(playerFired, toolHandle, endPosition)
end
end
function damageCharacter(playerFired, characterToDamage, hitPosition)
local humanoid = characterToDamage:FindFirstChildWhichIsA("Humanoid")
local validShot = isHitValid(playerFired, characterToDamage, hitPosition)
if humanoid and validShot then
-- Hapus kesehatan dari karakter
humanoid.Health -= LASER_DAMAGE
end
end
-- Hubungkan peristiwa ke fungsi yang tepat
eventsFolder.DamageCharacter.OnServerEvent:Connect(damageCharacter)
eventsFolder.LaserFired.OnServerEvent:Connect(playerFiredLaser)
Manajer Laser Klient
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local LaserRenderer = require(Players.LocalPlayer.PlayerScripts:WaitForChild("LaserRenderer"))
local eventsFolder = ReplicatedStorage.Events
-- Tampilkan laser pemain lain
local function createPlayerLaser(playerWhoShot, toolHandle, endPosition)
if playerWhoShot ~= Players.LocalPlayer then
LaserRenderer.createLaser(toolHandle, endPosition)
end
end
eventsFolder.LaserFired.OnClientEvent:Connect(createPlayerLaser)