Dalam tutorial ini, Anda akan belajar cara mengirim laser dari blaster di Creating Player Tools dan mendeteksi apakah atau tidak menghantam pemain.
Raycasting untuk Menemukan Tabrakan
Raycasting menciptakan sinar yang tak terlihat dari posisi awal menuju arah tertentu dengan panjang yang ditentukan. Jika sinar itu bertabrakan dengan objek atau tanah di jalannya, itu akan mengembalikan informasi tentang tabrakan seperti posisi dan objek yang bertabrakan.
Menemukan Lokasi Tikus
Sebelum laser dapat ditembak, Anda harus terlebih dahulu tahu di mana pemain bertujuan. Ini dapat ditemukan dengan raycasting dari lokasi 2D pemain di layar langsung ke depan dari kamera ke dunia permainan. Ray akan bertabrakan dengan apa pun yang dituju pemain dengan mouse.
Buka script ToolController di dalam alat Blaster dari Buat Alat Pemain. Jika Anda belum menyelesaikan tutorial tersebut, Anda dapat mengunduh model Blaster dan menyisipkannya ke StarterPack.
Pada bagian atas skrip, declare konstan bernama MAX_MOUSE_DISTANCE dengan nilai 1000 .
Buat fungsi bernama 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 acara ke fungsi yang sesuaitool.Equipped:Connect(toolEquipped)tool.Activated:Connect(toolActivated)Gunakan fungsi GetMouseLocation dari UserInputService untuk mendapatkan lokasi mouse 2D pemain di layar. Assign 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 dikenal, X dan Y 属性 dapat digunakan sebagai parameter untuk fungsi Camera:ViewportPointToRay() , yang menciptakan sebuah 1> Datatype.Ray1> dari layar ke dunia game 3D.
Gunakan X dan Y 属性 dari mouseLocation sebagai argument untuk fungsi 1> Class.Camera:ViewportPointToRay()|ViewportPointToRay()1> ini. Berikan ini ke variabel bernama 4> screenToWorldRay 4> .
local function getWorldMousePosition()local mouseLocation = UserInputService:GetMouseLocation()-- Buat sebuah sinar dari lokasi mouse 2Dlocal screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)end
Saatnya menggunakan fungsi Raycast untuk memeriksa apakah sinar menghantam objek. Ini mengharuskan posisi awal dan vektor arah: dalam contoh ini, Anda akan menggunakan propori asal dan arah dari screenToWorldRay .
Panjang vektor arah menentukan seberapa jauh sinar akan berpergian. Sinar harus panjang seperti MAX_MOUSE_DISTANCE, jadi Anda harus menggandakan vektor arah dengan MAX_MOUSE_DISTANCE.
Deklarasikan variabel bernama directionVector dan asignsinya nilai screenToWorldRay.Direction dengan pengganda MAX_MOUSE_DISTANCE.
local function getWorldMousePosition()local mouseLocation = UserInputService:GetMouseLocation()-- Buat sebuah sinar dari 2D mouseLocationlocal screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)-- Vektor arah unit dari sinar dibandingkan dengan jarak maksimumlocal directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCEPanggil fungsi Raycast ruang kerja, menyebutkan propinsi Origin dari screenToWorldRay sebagai argumen pertama dan 1> durationVector1> sebagai argumen kedua. Atribusikan ini ke variabel bernama 4> raycastResult4>.
local function getWorldMousePosition()local mouseLocation = UserInputService:GetMouseLocation()-- Buat sebuah sinar dari 2D mouseLocationlocal screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)-- Vektor arah unit dari sinar dibandingkan dengan jarak maksimumlocal directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE-- Raycast dari arah asalnya ke arahnyalocal raycastResult = workspace:Raycast(screenToWorldRay.Origin, directionVector)
Informasi Tabrakan
Jika operasi raycast menemukan objek yang dihantam oleh ray, itu akan mengembalikan RaycastResult, yang berisi informasi tentang kolisi antara ray dan objek.
RaycastResult PROperti | Deskripsi |
---|---|
Instansi | Class.BasePart atau Terrain sel yang diintervensi oleh sinar. |
Posisi | Di mana persimpangan terjadi; biasanya titik langsung di permukaan bagian atau tanah. |
Bahan | Bahan di titik bertabrakan. |
Biasa | Vektor normal wajah yang terpotong. Ini dapat digunakan untuk menentukan arah wajah yang menunjuk. |
Prop Posisi akan menjadi posisi objek yang mouse sedang menghover. Jika mouse tidak menghover over apa pun objek dalam jangkauan MAX_MOUSE_DISTANCE , raycastResult akan menjadi nol.
Buat pernyataan if untuk memeriksa apakah raycastResult ada.
Jika raycastResult memiliki nilai, return Posisi property.
Jika raycastResult adalah nol maka cari akhir dari raycast. Hitung posisi 3D mouse dengan menambahkan screenToWorldRay.Origin dan directionVector bersama-sama.
local function getWorldMousePosition()
local mouseLocation = UserInputService:GetMouseLocation()
-- Buat sebuah sinar dari 2D mouseLocation
local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
-- Vektor arah unit dari sinar dibandingkan dengan jarak maksimum
local directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE
-- Raycast dari arah asalnya ke arahnya
local raycastResult = workspace:Raycast(screenToWorldRay.Origin, directionVector)
if raycastResult then
-- Return point 3D persimpangan
return raycastResult.Position
else
-- Tidak ada objek yang dihantam jadi hitung posisi di ujung raya
return screenToWorldRay.Origin + directionVector
end
end
Menembak ke Arah Target
Sekarang posisi mouse 3D diketahui, itu dapat digunakan sebagai posisi posisi target untuk menembak laser ke arah. Raya kedua dapat dilemparkan antara senjata pemain dan posisi menembak menggunakan fungsi Raycast.
Deklarasikan konstan bernama MAX_LASER_DISTANCE di bagian atas script dan asign ke 500 , atau range yang Anda pilih untuk laser blaster.
local UserInputService = game:GetService("UserInputService")local tool = script.Parentlocal MAX_MOUSE_DISTANCE = 1000local MAX_LASER_DISTANCE = 500Buat fungsi bernama fireWeapon di bawah fungsi getWorldMousePosition.
Panggil getWorldMousePosition dan asign hasilnya ke variabel bernama mousePosition . Ini akan menjadi posisi target untuk raycast.
-- Tidak ada objek yang dihantam jadi hitung posisi di ujung rayareturn screenToWorldRay.Origin + directionVectorendendlocal function fireWeapon()local mouseLocation = getWorldMousePosition()endlocal function toolEquipped()tool.Handle.Equip:Play()end
Kali ini, vector arah untuk fungsi raycast akan mewakili arah dari posisi alat pemain ke lokasi target.
Deklarasikan variabel bernama targetDirection dan hitung vektor arah dengan mengurangkan posisi alat dari mouseLocation .
Normalisasi vektor dengan menggunakan prop性 Unit nya. Ini memberi kalibrasi skala 1, yang membuatnya mudah untuk di multiplykan nanti.
local function fireWeapon()local mouseLocation = getWorldMousePosition()-- Hitung vektor arah normal dan gandakan dengan jarak laserlocal targetDirection = (mouseLocation - tool.Handle.Position).UnitendDeklarasikan variabel bernama directionVector dan asign ke itu targetDirection multiplied by the MAX_LASER_DISTANCE .
local targetDirection = (mouseLocation - tool.Handle.Position).Unit-- Arah untuk menembak senjata, dikalikan dengan jarak maksimumlocal directionVector = targetDirection * MAX_LASER_DISTANCEend
Objek RaycastParams dapat digunakan untuk menyimpan parameter tambahan untuk fungsi raycast. Itu akan digunakan di laser blaster Anda untuk memastikan bahwa raycast tidak secara tidak sengaja bertabrakan dengan pemain yang menembak senjata. Setiap bagian yang termasuk dalam properti Dat
Lanjutkan fungsi fireWeapon dan declare variabel bernama weaponRaycastParams . Atribusikan objek RaycastParams baru ke itu.
Buat tabel yang berisi karakter lokal pemain dan mapasinya ke properti weaponRaycastParams.FilterDescendantsInstances .
Raycast dari posisi handle alat pemain, dalam arah ke arah directionVector . Ingat untuk menambahkan weaponRaycastParams sebagai argumen kali ini. Menetapkan 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 normal dan gandakan dengan jarak laser
local targetDirection = (mouseLocation - tool.Handle.Position).Unit
-- Arah untuk menembak senjata dikalikan dengan jarak maksimum
local directionVector = targetDirection * MAX_LASER_DISTANCE
-- Abaikan karakter pemain untuk mencegah mereka merusak diri
local weaponRaycastParams = RaycastParams.new()
weaponRaycastParams.FilterDescendantsInstances = {Players.LocalPlayer.Character}
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)
end
Akhirnya, Anda harus memeriksa bahwa operasi raycast menghasilkan nilai. Jika nilai dikembalikan, objek dibantu oleh sinar dan dapat dibuat antara senjata dan lokasi pukul. Jika tidak ada yang dikembalikan, posisi akhir perlu dihitung untuk menciptakan laser.
Deklarasikan variabel kosong bernama hitPosition .
Gunakan pernyataan jika untuk memeriksa apakah weaponRaycastResult memiliki nilai. Jika objek dibutuhkan, asign weaponRaycastResult.Position ke 1> hitPosition1> .
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)-- Periksa apakah objek apa pun dihantam di antara posisi awal dan akhirlocal hitPositionif weaponRaycastResult thenhitPosition = weaponRaycastResult.PositionendJika weaponRaycastResult tidak memiliki nilai, hitung posisi akhir dari raycast dengan menambahkan posisi dari tangan alat dengan duration dengan duration . Atribusikan ini ke 1> hitPosition 1> .
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)-- Periksa apakah objek apa pun dihantam di antara posisi awal dan akhirlocal hitPositionif weaponRaycastResult thenhitPosition = weaponRaycastResult.Positionelse-- Hitung posisi akhir berdasarkan jarak laser maksimumhitPosition = tool.Handle.Position + directionVectorendendNavigate to the toolActivated function and call the fireWeapon function so that the laser fires each time the tool is activated.
local function toolActivated()tool.Handle.Activate:Play()fireWeapon()end
Memeriksa Objek yang Dihantam
Untuk menemukan apakah objek yang dipukul oleh laser adalah bagian dari karakter pemain atau hanya potongan scenery, Anda harus mencari Humanoid, karena setiap karakter memilikinya.
Pertama, Anda harus menemukan model karakter . Jika bagian dari karakter terkena, Anda tidak dapat menganggap orang tua dari objek terkena akan menjadi karakter. Laser dapat menghantam bagian tubuh, aksesori, atau alat, semua dari yang berlokasi di berbagai bagian dari hierarki karakter.
Anda dapat menggunakan FindFirstAncestorOfClass untuk menemukan model karakter yang lebih tua dari objek yang dihantam oleh laser, jika ada. Jika Anda menemukan model dan itu berisi humanoid, dalam kebanyakan kasus Anda dapat menganggapnya sebagai karakter.
Tambahkan kode yang ditunjukkan di bawah ini ke weaponRaycastResult jika pernyataan untuk memeriksa apakah karakter tersebut terkena.
-- Periksa apakah objek apa pun dihantam di antara posisi awal dan akhirlocal hitPositionif weaponRaycastResult thenhitPosition = weaponRaycastResult.Position-- Instansi yang dihantam akan menjadi anak model karakter-- Jika humanoid ditemukan dalam model maka kemungkinan itu 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 menghasilkan Player hit ke jendela keluaran setiap kali operasi raycast menghantam pemain lain.
Menguji dengan Beberapa Pemain
Dua pemain diperlukan untuk menguji apakah silah raycast menemukan pemain lain, jadi Anda perlu memulai server lokal.
Pilih tab Pengujian di Studio.
Pastikan dropdown pemain di set 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, tes menembak pemain lain dengan senjata dengan mengklik pemain tersebut. "Player hit" harus ditampilkan dalamOutput setiap kali seorang pemain ditembak.
Anda dapat mengetahui lebih lanjut tentang tab Pengujian di sini .
Menemukan Posisi Laser
Blaster harus menembakkan balok cahaya merah di targetnya. Fungsi untuk ini akan berada di dalam ModuleScript sehingga dapat digunakan di skrip lain nanti. Pertama, script akan perlu menemukan lokasi di mana laser beam harus diputar.
Buat ModuleScript bernama LaserRender , yang merupakan anak dari StarterPlayerScripts di bawah StarterPlayer.
Buka script dan ganti nama tabel module ke nama script LaserReceiver .
Deklarasikan variabel bernama SHOT_DURATION dengan nilai 0.15 . Ini akan menjadi jumlah waktu (dalam detik) laser terlihat.
Buat fungsi LaserRender bernama createLaser dengan dua parameter bernama toolHandle dan endPosition .
local LaserRenderer = {}local SHOT_DURATION = 0.15 -- Waktu yang laser terlihat untuk-- Buat sinar laser dari posisi awal menuju posisi akhirfunction LaserRenderer.createLaser(toolHandle, endPosition)endreturn LaserRendererDeklarasikan variabel bernama startPosition dan tetapkan Position property dari toolHandle sebagai nilainya. Ini akan menjadi posisi laser pemain.
Deklarasikan variabel bernama laserDistance dan kurangkan endPosition dari startPosition untuk menemukan perbedaan antara kedua vector ini. Gunakan property 1> Magnitude1> ini untuk mendapatkan panjang sinar laser.
function LaserRenderer.createLaser(toolHandle, endPosition)local startPosition = toolHandle.Positionlocal laserDistance = (startPosition - endPosition).MagnitudeendDeklarasikan variabel laserCFrame untuk menyimpan posisi dan orientasi sinar laser. Posisi harus menjadi titik tengah dari awal dan akhir sinar. Gunakan CFrame.lookAt untuk menciptakan Datatype.C
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
Membuat Bagian Laser
Sekarang Anda tahu di mana untuk membuat sinaran laser, Anda perlu menambahkan sinaran laser itu sendiri. Ini dapat dilakukan dengan mudah dengan bagian Neon.
Deklarasikan variabel laserPart dan asign kepada itu sebuah instansi baru Part .
Tetapkan propperti berikut dari laserPart :
- Ukuran : Vector3.new(0.2, 0.2, laserDistance)
- CFrame : laserCFrame
- Terancang : benar
- BisaSalinan : false
- Warna : Color3.fromRGB(225, 0, 0) (warna merah kuat)
- Bahan : Enum.Material.Neon
Orang tua laserPart ke Ruang kerja .
Tambahkan bagian ke layanan Debris sehingga itu 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 sinar laser ke layanan Debris untuk dihapus & dibersihkanDebris:AddItem(laserPart, SHOT_DURATION)end
Sekarang fungsi untuk menyelesaikan sinar laser selesai, itu bisa dibangun oleh ToolController .
Pada bagian atas ToolController script, declare variabel bernama LaserReceiver dan require the LaserRenderModuleScript located in PlayerScripts.
local UserInputService = game:GetService("UserInputService")local Players = game:GetService("Players")local LaserRenderer = require(Players.LocalPlayer.PlayerScripts.LaserRenderer)local tool = script.ParentPada bagian bawah dari fungsi fireWeapon, panggil fungsi LaserRender 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 Bermain. Laser harus terlihat di antara senjata dan mouse saat alat ini diaktifkan.
Mengontrol Kecepatan Serangan Senjata
Senjata memerlukan ketinggalan antara setiap tembakan untuk mencegah pemain menghasilkan terlalu banyak kerusakan dalam waktu yang singkat. Ini dapat diperiksa dengan memeriksa apakah cukup waktu telah berlalu sejak pemain terakhir menembak.
Deklarasikan variabel di bagian atas ToolController bernama 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 bernama timeOfPreviousShot dengan nilai 0 . Ini menyimpan waktu terakhir pemain menembak dan akan diperbarui setiap tembakan.
local MAX_MOUSE_DISTANCE = 1000local MAX_LASER_DISTANCE = 300local FIRE_RATE = 0.3local timeOfPreviousShot = 0Buat fungsi bernama dapat menembakkan senjata tanpa parameter. Fungsi ini akan melihat berapa banyak waktu telah berlalu sejak tembakan sebelumnya dan mengembalikan benar atau salah.
local FIRE_RATE = 0.3local timeOfPreviousShot = 0-- Periksa apakah cukup waktu telah berlalu sejak tembakan sebelumnya dijatuhkanlocal function canShootWeapon()endlocal function getWorldMousePosition()Dalam fungsi, declare variabel bernama currentTime ; menetapkan hasil panggilan fungsi tick() . Ini mengembalikan berapa banyak waktu telah berlalu, dalam detik, sejak 1 Januari 1970 (tanggal yang mudah dipahami untuk menghitung waktu).
Kurangi timeOfPreviousShot dari currentTime dan kembalikan false jika hasilnya lebih kecil dari 1> FIRE_RATE1>; jika tidak, kembalikan 4> true4> .
-- Periksa apakah cukup waktu telah berlalu sejak tembakan sebelumnya dijatuhkanlocal function canShootWeapon()local currentTime = tick()if currentTime - timeOfPreviousShot < FIRE_RATE thenreturn falseendreturn trueendPada akhir fungsi fireWeapon, update timeOfPreviousShot setiap kali senjata diperluncurkan menggunakan tick.
hitPosition = tool.Handle.Position + directionVectorendtimeOfPreviousShot = tick()LaserRenderer.createLaser(tool.Handle, hitPosition)endDalam fungsi toolActivated, buat pernyataan jika dan panggil canShootWeapon untuk memeriksa apakah senjata dapat diperlakukan.
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 0,3 detik pinggiran antara setiap tembakan.
Menghancurkan Pemain
Klien tidak dapat merusak klien lain secara langsung; server harus bertanggung jawab atas penerbitan kerusakan saat seorang pemain terkena.
Klien dapat menggunakan RemoteEvent untuk memberi tahu server bahwa karakter telah dihantam. Ini harus disimpan di ReplicatedStorage , di mana mereka dapat dilihat oleh klien dan server.
Buat Folder di ReplicatedStorage bernama Acara .
Sisipkan RemoteEvent ke dalam folder Acara dan bernama DamageCharacter .
Di ToolController , buat variabel di awal script 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 "Player hit" pernyataan pencetakan di fireWeapon dengan garis Lua untuk menembakkan acara DamageCharacter jarak dengan variabel 1> characterModel1> 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 harus menangani kerusakan pada pemain yang telah terkena saat acara diaktifkan.
Sisipkan Skrip ke ServerScriptService dan nama nya ServerLaserManager.
Deklarasikan variabel bernama LASER_DAMAGE dan tetapkan ke 10 , atau nilai pilihan Anda.
Buat fungsi bernama damageCharacter dengan dua parameter bernama playerFired dan karakterToDamage .
Di dalam fungsi, temukan Humanoid karakter dan kurangkan LASER_DAMAGE dari kesehatannya.
Hubungkan fungsi damageCharacter ke acara jauh DamageCharacter 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 acara ke fungsi yang sesuaieventsFolder.DamageCharacter.OnServerEvent:Connect(damageCharacter)Uji blaster dengan 2 pemain dengan memulai server lokal. Saat Anda menembak pemain lain, kesehatan mereka akan berkurang oleh jumlah yang ditentukan untuk LASER_DAMAGE .
Mengubah Laser Pemain Lain
Saat ini, sinar laser dibuat oleh klien yang menembak senjata, jadi hanya mereka yang akan dapat melihat sinar laser.
Jika sinar laser dibuat di server maka semua orang akan dapat melihatnya. Namun, akan ada sedikit ketinggalan antara klien yang menembak senjata dan server menerima informasi tentang tembakan. Ini berarti klien menembak senjata akan melihat ketinggalan antara ketika mereka mengaktifkan senjata dan ketika mereka melihat sinar laser; senjata akan terasa tertinggal.
Untuk menyelesaikan masalah ini, setiap klien akan menciptakan sinaran laser mereka sendiri. Ini berarti klien yang menembak senjata akan melihat sinaran laser secara instan. Klien lain akan mengalami kesalahan waktu kecil antara ketika seorang pemain menembak dan sinaran muncul. Ini adalah skenario kasus terbaik: tidak ada cara untuk berkomunikasi antara klien Laser satu ke klien lainnya lebih cepat.
Klien Pemotret
Pertama, klien perlu memberi tahu server bahwa telah menembakkan laser dan memberikan posisi akhir.
Sisipkan RemoteEvent ke dalam folder Acara di ReplicatedStorage dan nama LaserFired.
Temukan fungsi fireWeapon di script ToolController . Pada akhir fungsi, tembak acara LaserFired menggunakan 1> hitPosition1> sebagai argumen.
hitPosition = tool.Handle.Position + directionVectorendtimeOfPreviousShot = tick()eventsFolder.LaserFired:FireServer(hitPosition)LaserRenderer.createLaser(tool.Handle, hitPosition)end
Server
Server sekarang harus menerima acara yang ditembak klien dan memberi tahu semua klien posisi awal dan akhir dari sinar laser sehingga mereka juga dapat menyederikannya.
Dalam ServerLaserManager script, buat fungsi bernama playerFiredLaser di atas damageCharacter dengan dua parameter bernama 2> playerFired2> dan 5> endPosition5> .
Hubungkan fungsi ke acara LaserFired jarak.
-- Beritahu semua klien bahwa laser telah diaktifkan sehingga mereka dapat menunjukkan laserlocal function playerFiredLaser(playerFired, endPosition)end-- Hubungkan acara ke fungsi yang sesuaieventsFolder.DamageCharacter.OnServerEvent:Connect(damageCharacter)eventsFolder.LaserFired.OnServerEvent:Connect(playerFiredLaser)
Server memerlukan posisi awal laser. Ini dapat dikirim dari klien, tetapi lebih baik menghindari kepercayaan klien di mana mungkin. Posisi pegang senjata karakter akan menjadi posisi awal, jadi server dapat menemukannya dari sana.
Buat fungsi mendapatkan tangan pemain di atas fungsi playerFiredLaser dengan parameter bernama player.
Gunakan kode berikut untuk mencari karakter pemain untuk senjata dan kembalikan objek handle.
local LASER_DAMAGE = 10-- Temukan pegangan alat yang dibawa pemainlocal function getPlayerToolHandle(player)local weapon = player.Character:FindFirstChildOfClass("Tool")if weapon thenreturn weapon:FindFirstChild("Handle")endend-- Beritahu semua klien bahwa laser telah diaktifkan sehingga mereka dapat menunjukkan laserlocal function playerFiredLaser(playerFired, endPosition)
Server sekarang dapat memanggil FireAllClients di acara remote LaserFired untuk mengirim informasi yang diperlukan untuk menyajikan laser kepada klien. Ini termasuk pemain yang menembak laser (jadi klien untuk pemain itu tidak menyajikan laser dua kali), dan posisi akhir dari laser.
Dalam fungsi playerFiredLaser, panggil fungsi getPlayerToolHandle dengan playerFired sebagai argumen dan atribuisikan nilai ke variabel bernama 1> toolHandle1> .
Jika toolHandle ada, fire the LaserFired event for all klien menggunakan playerFired , toolHandle dan 1> endPosition1> sebagai argument.
-- Beritahu semua klien bahwa laser telah diaktifkan sehingga mereka dapat menunjukkan laserlocal function playerFiredLaser(playerFired, endPosition)local toolHandle = getPlayerToolHandle(playerFired)if toolHandle theneventsFolder.LaserFired:FireAllClients(playerFired, toolHandle, endPosition)endend
Render di Klien
Sekarang FireAllClients telah dipanggil, setiap klien akan menerima acara dari server untuk menyajikan sinaran laser. Setiap klien dapat menggunakan modul LaserRenderer dari posisi dan posisi akhir klien yang diberikan oleh server untuk menyajikan sinaran laser. Pemain yang menembak sinaran laser di tempat pertama harus mengabaikan acara ini jika
Buat Skrip Lokal di StarterPlayerScripts bernama ClientLaserManager .
Dalam script, butuhkan modul LaserRender .
Buat fungsi bernama createPlayerLaser dengan parameter playerWhoShot , toolHandle dan 1> endPosition1> .
Hubungkan fungsi ke acara LaserFired di Folder Acara.
Dalam fungsi, gunakan pernyataan jika untuk memeriksa apakah playerWhoShot tidak sama dengan Pemain Lokal.
Dalam if statement, call the createLaser function from the LaserRender module using toolHandle and endPosition as arguments.
local Players = game:GetService("Players")local ReplicatedStorage = game:GetService("ReplicatedStorage")local LaserRenderer = require(script.Parent:WaitForChild("LaserRenderer"))local eventsFolder = ReplicatedStorage.Events-- Menampilkan 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. Posisi setiap klien di berbagai sisi monitor sehingga Anda dapat melihat kedua jendela sekaligus. Saat Anda menembak di satu klien, Anda harus melihat laser di klien lain.
Efek Suara
Efek suara tembak saat ini hanya bermain di klien yang menembak proyektil. Anda akan perlu mengubah kode untuk memutar suara sehingga pemain lain juga mendengarnya.
Dalam naskah ToolController , buka fungsi toolActivated dan hapus baris yang memutar suara Aktifkan.
local function toolActivated()if canShootWeapon() thenfireWeapon()endendPada bagian bawah dari fungsi createLaser di LaserRender , declare variabel bernama shootingSound dan gunakan metode 1> Class.Instance:FindFirstChild()|FindFirstChild()1> dari 4> toolHandle4> untuk memeriksa untuk suara 7>Aktifkan7>.
Gunakan pernyataan jika untuk memeriksa apakah shootingSound ada; jika demikian, panggil fungsi Mainkan nya.
laserPart.Parent = workspace-- Tambahkan sinar laser ke layanan Debris untuk dihapus & dibersihkanDebris:AddItem(laserPart, SHOT_DURATION)-- Mainkan suara tembak senjatalocal shootingSound = toolHandle:FindFirstChild("Activate")if shootingSound thenshootingSound:Play()endend
Mengamankan Jarak Menggunakan Validasi
Jika server tidak mengambil data dari permintaan yang 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 bentuk saat ini, acara DamageCharacter jauh lebih rentan terhadap serangan. Peretas bisa menggunakan acara ini untuk menyebabkan kerusakan pada pemain mana pun yang mereka inginkan dalam permainan tanpa menembak mereka.
Validasi adalah proses memeriksa bahwa nilai yang dikirim ke server realistis. Dalam hal ini, server akan perlu:
- Periksa apakah jarak antara pemain dan posisi yang dihantam oleh laser berada dalam batas tertentu.
- Raycast di antara senjata yang menembak laser dan posisi pukul untuk memastikan bahwa tembakan itu mungkin dan tidak melalui tembok apa pun.
Klien
Klien perlu mengirimkan server ke posisi yang dihantam oleh raycast sehingga dapat memeriksa jaraknya realistis.
Di ToolController , navigasilah ke garis di mana acara remote DamageCharacter diaktifkan dalam fungsi fireWeapon.
Tambahkan hitPosition sebagai argumen.
if characterModel thenlocal humanoid = characterModel:FindFirstChildWhichIsA("Humanoid")if humanoid theneventsFolder.DamageCharacter:FireServer(characterModel, hitPosition)endend
Server
Klien sekarang mengirim parameter tambahan melalui acara remote DamageCharacter, jadi ServerLaserManager perlu disesuaikan untuk menerimanya.
Dalam ServerLaserManager script, 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 , 1> characterToDamage1> dan 4> hitPosition4> .
endlocal function isHitValid(playerFired, characterToDamage, hitPosition)end
Pemeriksaan pertama akan menjadi jarak antara posisi hit dan karakter hit.
Deklarasikan variabel bernama MAX_HIT_PROXIMITY di bagian atas script dan menetapkan nilainya sebagai 10 . Ini akan menjadi jarak maksimum yang diizinkan antara hit dan karakter. Toleransi diperlukan karena karakter mungkin telah bergerak sedikit sejak klien menjalankan acara.
local ReplicatedStorage = game:GetService("ReplicatedStorage")local eventsFolder = ReplicatedStorage.Eventslocal LASER_DAMAGE = 10local MAX_HIT_PROXIMITY = 10Dalam fungsi isHitValid, hitung jarak antara karakter dan posisi hit. Jika jaraknya lebih besar dari MAX_HIT_PROXIMITY maka return false .
local function isHitValid(playerFired, characterToDamage, hitPosition)-- Validasi jarak antara karakter hit dan posisi hitlocal characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitudeif characterHitProximity > MAX_HIT_PROXIMITY thenreturn falseendend
Pemeriksaan kedua akan melibatkan raycast antara senjata yang ditembak dan posisi hit. Jika raycast mengembalikan objek yang bukan karakter, Anda dapat menganggap bahwa tembakan tidak valid karena sesuatu mengblokir tembakan.
Salinan kode di bawah ini untuk melakukan periksaini. Kembalikan benar di akhir fungsi: jika mencapai akhiri, semua tindakan telah berhasil.
local function isHitValid(playerFired, characterToDamage, hitPosition)-- Validasi jarak antara karakter hit dan posisi hitlocal 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 abai tembakanif rayResult and not rayResult.Instance:IsDescendantOf(characterToDamage) thenreturn falseendendreturn trueendDeklarasikan variabel dalam fungsi damageCharacter yang bernama validShot . Berikan hasil dari panggilan ke fungsi isHitValid dengan tiga argumen: 1> playerFired1> , 4> characterToDamage4> dan 7> hitPosition7> .
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 damageCharacter lebih aman dan akan mencegah kebanyakan pemain menyalahgunakannya. Catat bahwa beberapa pemain jahat akan sering menemukan cara di sekitar validasi; menjaga acara remote aman adalah upaya berkelanjutan.
Laser blaster Anda sekarang selesai, dengan sistem deteksi pukulan dasar menggunakan raycasting. Coba tutorial Mendeteksi Masukan Pengguna untuk menemukan cara Anda dapat menambahkan tindakan pemuatan ulang ke laser blaster Anda, atau buat peta permainan menyenangkan dan coba laser blaster Anda dengan pemain lain!
Kode Terakhir
Kontrol Alat
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 dijatuhkan
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 sebuah sinar dari lokasi mouse 2D
local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
-- Vektor arah unit dari sinar dibandingkan 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
-- Return point 3D persimpangan
return raycastResult.Position
else
-- Tidak ada objek yang dihantam jadi hitung posisi di ujung raya
return screenToWorldRay.Origin + directionVector
end
end
local function fireWeapon()
local mouseLocation = getWorldMousePosition()
-- Hitung vektor arah normal dan gandakan dengan jarak laser
local targetDirection = (mouseLocation - tool.Handle.Position).Unit
-- Arah untuk menembak senjata, dikalikan dengan jarak maksimum
local directionVector = targetDirection * MAX_LASER_DISTANCE
-- Abaikan karakter pemain untuk mencegah mereka merusak diri
local weaponRaycastParams = RaycastParams.new()
weaponRaycastParams.FilterDescendantsInstances = {Players.LocalPlayer.Character}
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)
-- Periksa apakah objek apa pun dihantam di antara posisi awal dan akhir
local hitPosition
if weaponRaycastResult then
hitPosition = weaponRaycastResult.Position
-- Instansi yang dihantam akan menjadi anak model karakter
-- Jika humanoid ditemukan dalam model maka kemungkinan itu 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)
Pemutar Laser
local LaserRenderer = {}
local Debris = game:GetService("Debris")
local SHOT_DURATION = 0.15 -- Waktu yang laser terlihat untuk
-- Buat sinar 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 sinar laser ke layanan Debris untuk dihapus & 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 pegangan alat yang dibawa 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)
-- Validasi jarak antara karakter hit dan posisi hit
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 abai tembakan
if rayResult and not rayResult.Instance:IsDescendantOf(characterToDamage) then
return false
end
end
return true
end
-- Beritahu semua klien bahwa laser telah diaktifkan sehingga mereka dapat menunjukkan 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 acara ke fungsi yang sesuai
eventsFolder.DamageCharacter.OnServerEvent:Connect(damageCharacter)
eventsFolder.LaserFired.OnServerEvent:Connect(playerFiredLaser)
Manajer Laser Klien
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local LaserRenderer = require(Players.LocalPlayer.PlayerScripts:WaitForChild("LaserRenderer"))
local eventsFolder = ReplicatedStorage.Events
-- Menampilkan 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)