Deteksi Hit dengan Laser

*Konten ini diterjemahkan menggunakan AI (Beta) dan mungkin mengandung kesalahan. Untuk melihat halaman ini dalam bahasa Inggris, klik di sini.

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.

Raycast dari A ke B bertabrakan dengan dinding

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.

  1. 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.

  2. Pada bagian atas skrip, declare konstan bernama MAX_MOUSE_DISTANCE dengan nilai 1000 .

  3. Buat fungsi bernama getWorldMousePosition .


    local tool = script.Parent
    local MAX_MOUSE_DISTANCE = 1000
    local function getWorldMousePosition()
    end
    local function toolEquipped()
    tool.Handle.Equip:Play()
    end
    local function toolActivated()
    tool.Handle.Activate:Play()
    end
    -- Hubungkan acara ke fungsi yang sesuai
    tool.Equipped:Connect(toolEquipped)
    tool.Activated:Connect(toolActivated)
  4. 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.Parent
    local MAX_MOUSE_DISTANCE = 1000
    local 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.

  1. 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 2D
    local 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.

  1. 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 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
  2. Panggil 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 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)

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 PROpertiDeskripsi
InstansiClass.BasePart atau Terrain sel yang diintervensi oleh sinar.
PosisiDi mana persimpangan terjadi; biasanya titik langsung di permukaan bagian atau tanah.
BahanBahan di titik bertabrakan.
BiasaVektor 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.

  1. Buat pernyataan if untuk memeriksa apakah raycastResult ada.

  2. Jika raycastResult memiliki nilai, return Posisi property.

  3. 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.

  1. 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.Parent
    local MAX_MOUSE_DISTANCE = 1000
    local MAX_LASER_DISTANCE = 500
  2. Buat fungsi bernama fireWeapon di bawah fungsi getWorldMousePosition.

  3. 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 raya
    return screenToWorldRay.Origin + directionVector
    end
    end
    local function fireWeapon()
    local mouseLocation = getWorldMousePosition()
    end
    local function toolEquipped()
    tool.Handle.Equip:Play()
    end

Kali ini, vector arah untuk fungsi raycast akan mewakili arah dari posisi alat pemain ke lokasi target.

  1. Deklarasikan variabel bernama targetDirection dan hitung vektor arah dengan mengurangkan posisi alat dari mouseLocation .

  2. 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 laser
    local targetDirection = (mouseLocation - tool.Handle.Position).Unit
    end
  3. Deklarasikan 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 maksimum
    local directionVector = targetDirection * MAX_LASER_DISTANCE
    end

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

  1. Lanjutkan fungsi fireWeapon dan declare variabel bernama weaponRaycastParams . Atribusikan objek RaycastParams baru ke itu.

  2. Buat tabel yang berisi karakter lokal pemain dan mapasinya ke properti weaponRaycastParams.FilterDescendantsInstances .

  3. 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.

  1. Deklarasikan variabel kosong bernama hitPosition .

  2. 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 akhir
    local hitPosition
    if weaponRaycastResult then
    hitPosition = weaponRaycastResult.Position
    end
  3. Jika 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 akhir
    local hitPosition
    if weaponRaycastResult then
    hitPosition = weaponRaycastResult.Position
    else
    -- Hitung posisi akhir berdasarkan jarak laser maksimum
    hitPosition = tool.Handle.Position + directionVector
    end
    end
  4. Navigate 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.

  1. 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 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
    print("Player hit")
    end
    end
    else
    -- Hitung posisi akhir berdasarkan jarak laser maksimum
    hitPosition = tool.Handle.Position + directionVector
    end

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.

  1. Pilih tab Pengujian di Studio.

  2. 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.

  3. 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.

  1. Buat ModuleScript bernama LaserRender , yang merupakan anak dari StarterPlayerScripts di bawah StarterPlayer.

  2. Buka script dan ganti nama tabel module ke nama script LaserReceiver .

  3. Deklarasikan variabel bernama SHOT_DURATION dengan nilai 0.15 . Ini akan menjadi jumlah waktu (dalam detik) laser terlihat.

  4. 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 akhir
    function LaserRenderer.createLaser(toolHandle, endPosition)
    end
    return LaserRenderer
  5. Deklarasikan variabel bernama startPosition dan tetapkan Position property dari toolHandle sebagai nilainya. Ini akan menjadi posisi laser pemain.

  6. 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.Position
    local laserDistance = (startPosition - endPosition).Magnitude
    end
  7. Deklarasikan 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.Position
    local laserDistance = (startPosition - endPosition).Magnitude
    local 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.

  1. Deklarasikan variabel laserPart dan asign kepada itu sebuah instansi baru Part .

  2. Tetapkan propperti berikut dari laserPart :

    1. Ukuran : Vector3.new(0.2, 0.2, laserDistance)
    2. CFrame : laserCFrame
    3. Terancang : benar
    4. BisaSalinan : false
    5. Warna : Color3.fromRGB(225, 0, 0) (warna merah kuat)
    6. Bahan : Enum.Material.Neon
  3. Orang tua laserPart ke Ruang kerja .

  4. Tambahkan bagian ke layanan Debris sehingga itu dihapus setelah jumlah detik dalam variabel SHOT_DURATION .


    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(225, 0, 0)
    laserPart.Material = Enum.Material.Neon
    laserPart.Parent = workspace
    -- Tambahkan sinar laser ke layanan Debris untuk dihapus & dibersihkan
    Debris:AddItem(laserPart, SHOT_DURATION)
    end

Sekarang fungsi untuk menyelesaikan sinar laser selesai, itu bisa dibangun oleh ToolController .

  1. 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.Parent
  2. Pada bagian bawah dari fungsi fireWeapon, panggil fungsi LaserRender createLaser menggunakan handle alat dan hitPosition sebagai argumen.


    -- Hitung posisi akhir berdasarkan jarak laser maksimum
    hitPosition = tool.Handle.Position + directionVector
    end
    LaserRenderer.createLaser(tool.Handle, hitPosition)
    end
  3. Uji 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.

  1. 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.

  2. 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 = 1000
    local MAX_LASER_DISTANCE = 300
    local FIRE_RATE = 0.3
    local timeOfPreviousShot = 0
  3. Buat 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.3
    local timeOfPreviousShot = 0
    -- Periksa apakah cukup waktu telah berlalu sejak tembakan sebelumnya dijatuhkan
    local function canShootWeapon()
    end
    local function getWorldMousePosition()
  4. 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).

  5. 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 dijatuhkan
    local function canShootWeapon()
    local currentTime = tick()
    if currentTime - timeOfPreviousShot < FIRE_RATE then
    return false
    end
    return true
    end
  6. Pada akhir fungsi fireWeapon, update timeOfPreviousShot setiap kali senjata diperluncurkan menggunakan tick.


    hitPosition = tool.Handle.Position + directionVector
    end
    timeOfPreviousShot = tick()
    LaserRenderer.createLaser(tool.Handle, hitPosition)
    end
  7. Dalam fungsi toolActivated, buat pernyataan jika dan panggil canShootWeapon untuk memeriksa apakah senjata dapat diperlakukan.


    local function toolActivated()
    if canShootWeapon() then
    tool.Handle.Activate:Play()
    fireWeapon()
    end
    end

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.

  1. Buat Folder di ReplicatedStorage bernama Acara .

  2. Sisipkan RemoteEvent ke dalam folder Acara dan bernama DamageCharacter .

  3. 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.Parent
    local eventsFolder = ReplicatedStorage.Events
    local MAX_MOUSE_DISTANCE = 1000
    local MAX_LASER_DISTANCE = 500
  4. Ganti "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 then
    local humanoid = characterModel:FindFirstChildWhichIsA("Humanoid")
    if humanoid then
    eventsFolder.DamageCharacter:FireServer(characterModel)
    end
    end
    else
    -- Hitung posisi akhir berdasarkan jarak laser maksimum
    hitPosition = tool.Handle.Position + directionVector
    end

Server harus menangani kerusakan pada pemain yang telah terkena saat acara diaktifkan.

  1. Sisipkan Skrip ke ServerScriptService dan nama nya ServerLaserManager.

  2. Deklarasikan variabel bernama LASER_DAMAGE dan tetapkan ke 10 , atau nilai pilihan Anda.

  3. Buat fungsi bernama damageCharacter dengan dua parameter bernama playerFired dan karakterToDamage .

  4. Di dalam fungsi, temukan Humanoid karakter dan kurangkan LASER_DAMAGE dari kesehatannya.

  5. Hubungkan fungsi damageCharacter ke acara jauh DamageCharacter di folder Acara.


    local ReplicatedStorage = game:GetService("ReplicatedStorage")
    local eventsFolder = ReplicatedStorage.Events
    local LASER_DAMAGE = 10
    function damageCharacter(playerFired, characterToDamage)
    local humanoid = characterToDamage:FindFirstChildWhichIsA("Humanoid")
    if humanoid then
    -- Hapus kesehatan dari karakter
    humanoid.Health -= LASER_DAMAGE
    end
    end
    -- Hubungkan acara ke fungsi yang sesuai
    eventsFolder.DamageCharacter.OnServerEvent:Connect(damageCharacter)
  6. 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.

  1. Sisipkan RemoteEvent ke dalam folder Acara di ReplicatedStorage dan nama LaserFired.

  2. Temukan fungsi fireWeapon di script ToolController . Pada akhir fungsi, tembak acara LaserFired menggunakan 1> hitPosition1> sebagai argumen.


    hitPosition = tool.Handle.Position + directionVector
    end
    timeOfPreviousShot = 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.

  1. Dalam ServerLaserManager script, buat fungsi bernama playerFiredLaser di atas damageCharacter dengan dua parameter bernama 2> playerFired2> dan 5> endPosition5> .

  2. Hubungkan fungsi ke acara LaserFired jarak.


    -- Beritahu semua klien bahwa laser telah diaktifkan sehingga mereka dapat menunjukkan laser
    local function playerFiredLaser(playerFired, endPosition)
    end

    -- Hubungkan acara ke fungsi yang sesuai
    eventsFolder.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.

  1. Buat fungsi mendapatkan tangan pemain di atas fungsi playerFiredLaser dengan parameter bernama player.

  2. Gunakan kode berikut untuk mencari karakter pemain untuk senjata dan kembalikan objek handle.


    local LASER_DAMAGE = 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
    -- Beritahu semua klien bahwa laser telah diaktifkan sehingga mereka dapat menunjukkan laser
    local 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.

  1. Dalam fungsi playerFiredLaser, panggil fungsi getPlayerToolHandle dengan playerFired sebagai argumen dan atribuisikan nilai ke variabel bernama 1> toolHandle1> .

  2. 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 laser
    local function playerFiredLaser(playerFired, endPosition)
    local toolHandle = getPlayerToolHandle(playerFired)
    if toolHandle then
    eventsFolder.LaserFired:FireAllClients(playerFired, toolHandle, endPosition)
    end
    end

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

  1. Buat Skrip Lokal di StarterPlayerScripts bernama ClientLaserManager .

  2. Dalam script, butuhkan modul LaserRender .

  3. Buat fungsi bernama createPlayerLaser dengan parameter playerWhoShot , toolHandle dan 1> endPosition1> .

  4. Hubungkan fungsi ke acara LaserFired di Folder Acara.

  5. Dalam fungsi, gunakan pernyataan jika untuk memeriksa apakah playerWhoShot tidak sama dengan Pemain Lokal.

  6. 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 lain
    local function createPlayerLaser(playerWhoShot, toolHandle, endPosition)
    if playerWhoShot ~= Players.LocalPlayer then
    LaserRenderer.createLaser(toolHandle, endPosition)
    end
    end
    eventsFolder.LaserFired.OnClientEvent:Connect(createPlayerLaser)
  7. 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.

  1. Dalam naskah ToolController , buka fungsi toolActivated dan hapus baris yang memutar suara Aktifkan.


    local function toolActivated()
    if canShootWeapon() then
    fireWeapon()
    end
    end
  2. Pada 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>.

  3. 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 & dibersihkan
    Debris:AddItem(laserPart, SHOT_DURATION)
    -- Mainkan suara tembak senjata
    local shootingSound = toolHandle:FindFirstChild("Activate")
    if shootingSound then
    shootingSound:Play()
    end
    end

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.

  1. Di ToolController , navigasilah ke garis di mana acara remote DamageCharacter diaktifkan dalam fungsi fireWeapon.

  2. Tambahkan hitPosition sebagai argumen.


    if characterModel then
    local humanoid = characterModel:FindFirstChildWhichIsA("Humanoid")
    if humanoid then
    eventsFolder.DamageCharacter:FireServer(characterModel, hitPosition)
    end
    end

Server

Klien sekarang mengirim parameter tambahan melalui acara remote DamageCharacter, jadi ServerLaserManager perlu disesuaikan untuk menerimanya.

  1. 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 karakter
    humanoid.Health -= LASER_DAMAGE
    end
    end
  2. Di bawah fungsi getPlayerToolHandle, buat fungsi bernama isHitValid dengan tiga parameter: playerFired , 1> characterToDamage1> dan 4> hitPosition4> .


    end
    local function isHitValid(playerFired, characterToDamage, hitPosition)
    end

Pemeriksaan pertama akan menjadi jarak antara posisi hit dan karakter hit.

  1. 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.Events
    local LASER_DAMAGE = 10
    local MAX_HIT_PROXIMITY = 10
  2. Dalam 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 hit
    local characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitude
    if characterHitProximity > MAX_HIT_PROXIMITY then
    return false
    end
    end

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.

  1. 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 hit
    local characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitude
    if characterHitProximity > 10 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
  2. Deklarasikan variabel dalam fungsi damageCharacter yang bernama validShot . Berikan hasil dari panggilan ke fungsi isHitValid dengan tiga argumen: 1> playerFired1> , 4> characterToDamage4> dan 7> hitPosition7> .

  3. 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 karakter
    humanoid.Health -= LASER_DAMAGE
    end
    end

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)