Orta seviye öğreticiler

Lazerlerle vurma tespiti

*Bu içerik, yapay zekâ (beta) kullanılarak çevrildi ve hatalar içerebilir. Sayfayı İngilizce görüntülemek için buraya tıkla.

Bu öğreticide, Oyun aracı oluştur kapsamında blaster'dan bir lazer nasıl fırlatılır ve bir oyuncuya çarpıp çarpmadığı nasıl tespit edilir, bunu öğreneceksiniz.

Çarpışmaları bulmak için ışın tarama

Işın tarama belirli bir uzunluğa sahip, başlangıç pozisyonundan belirli bir yöne doğru görünmez bir ışın yaratır. Işın geçişi sırasında nesnelere veya yüzeye çarparsa, çarpma ile ilgili konum gibi bilgiler döner.

A'dan B'ye ışın tarama, duvara çarpıyor

Fare konumunu bul

Bir lazer ateşlenmeden önce, oyuncunun nereye nişan aldığını bilmeniz gerekir. Bu, oyuncunun ekran üzerindeki 2D fare konumundan, kameradan 3D dünyaya doğru ışın tarama yaparak bulunabilir. Işın, oyuncunun fare ile hedef aldığı nesneye çarpacaktır.

  1. Oyun aracı oluştur içindeki Blaster aracının içindeki ToolController betiğini açın. Eğer o öğretimi tamamlamadıysanız Blaster modelini indirebilir ve StarterPack'e ekleyebilirsiniz.

  2. Betiğin en üstünde, değeri 1000 olan MAX_MOUSE_DISTANCE adında bir sabit tanımlayın.

  3. getWorldMousePosition adında bir fonksiyon oluşturun.


    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
    -- Olayları uygun fonksiyonlara bağlayın
    tool.Equipped:Connect(toolEquipped)
    tool.Activated:Connect(toolActivated)
  4. UserInputService'in GetMouseLocation fonksiyonunu kullanarak, oyuncunun ekrandaki 2D fare konumunu alın. Bunu mouseLocation adlı bir değişkene atayın.


    local UserInputService = game:GetService("UserInputService")
    local tool = script.Parent
    local MAX_MOUSE_DISTANCE = 1000
    local function getWorldMousePosition()
    local mouseLocation = UserInputService:GetMouseLocation()
    end

Artık 2D fare konumu bilindiğine göre, X ve Y özellikleri Camera:ViewportPointToRay() fonksiyonuna parametre olarak kullanılabilir, bu da ekranın 3D dünyaya doğru bir Ray oluşturur.

  1. mouseLocation'in X ve Y özelliklerini Camera:ViewportPointToRay() fonksiyonunun argümanları olarak kullanın. Bunu screenToWorldRay adlı bir değişkene atayın.


    local function getWorldMousePosition()
    local mouseLocation = UserInputService:GetMouseLocation()
    -- 2D fare konumundan bir ışın oluştur
    local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
    end

Artık WorldRoot:Raycast() fonksiyonunu kullanarak ışının bir nesneye çarpıp çarpmadığını kontrol etme zamanıdır. Bu, bir başlangıç pozisyonu ve bir yön vektörü gerektirir: bu örnekte, screenToWorldRay'in origin ve direction özelliklerini kullanacaksınız.

Yön vektörünün uzunluğu, ışının ne kadar yol alacağını belirler. Işın, MAX_MOUSE_DISTANCE kadar uzun olmalıdır, bu nedenle yön vektörünü MAX_MOUSE_DISTANCE ile çarpmanız gerekir.

  1. directionVector adlı bir değişken tanımlayın ve değerini screenToWorldRay.Direction ile MAX_MOUSE_DISTANCE çarpımına atayın.


    local function getWorldMousePosition()
    local mouseLocation = UserInputService:GetMouseLocation()
    -- 2D fare konumundan bir ışın oluştur
    local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
    -- Işının birim yön vektörü, maksimum mesafe ile çarpılmış
    local directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE
  2. workspace'in WorldRoot:Raycast() fonksiyonunu çağırın ve ilk argüman olarak screenToWorldRay'in Origin özelliğini ve ikinci argüman olarak directionVector'ı verin. Bunu raycastResult adlı bir değişkene atayın.


    local function getWorldMousePosition()
    local mouseLocation = UserInputService:GetMouseLocation()
    -- 2D fare konumundan bir ışın oluştur
    local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
    -- Işının birim yön vektörü, maksimum mesafe ile çarpılmış
    local directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE
    -- Işının orijinal yönüne doğru ışın tarama yap
    local raycastResult = workspace:Raycast(screenToWorldRay.Origin, directionVector)

Çarpışma bilgisi

Eğer ışın tarama işlemi ışına çarpan bir nesne bulursa, bir RaycastResult döndürür; bu nesne, ışın ve nesne arasındaki çarpışmalar hakkında bilgi içerir.

RaycastResult ÖzelliğiAçıklama
InstanceIşının kesiştiği BasePart veya Terrain hücresi.
PositionKesişmenin gerçekleştiği yer; genellikle bir parça veya arazinin yüzeyinde doğrudan bir nokta.
MaterialKesişme noktasındaki malzeme.
NormalKesişen yüzeyin normal vektörü. Bu, yüzeyin hangi yöne baktığını belirlemek için kullanılabilir.

Position özelliği, farenin üzerine geldiği nesnenin konumu olacaktır. Eğer fare, MAX_MOUSE_DISTANCE mesafesindeki hiçbir nesneye çarpmıyorsa, raycastResult nil olur.

  1. raycastResult'ın var olup olmadığını kontrol etmek için bir if durumu oluşturun.

  2. Eğer raycastResult değere sahipse, onun Position özelliğini döndürün.

  3. Eğer raycastResult nil ise, ışın taramanın sonunu bulun. Fare ile 3D konumunu hesaplamak için screenToWorldRay.Origin ile directionVector'ı toplayın.


local function getWorldMousePosition()
local mouseLocation = UserInputService:GetMouseLocation()
-- 2D fare konumundan bir ışın oluştur
local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
-- Işının birim yön vektörü, maksimum mesafe ile çarpılmış
local directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE
-- Işının orijinal yönüne doğru ışın tarama yap
local raycastResult = workspace:Raycast(screenToWorldRay.Origin, directionVector)
if raycastResult then
-- Kesiti döndür
return raycastResult.Position
else
-- Hiçbir nesneye çarpılmadı, bu nedenle ışının sonundaki konumu hesapla
return screenToWorldRay.Origin + directionVector
end
end

Hedefe ateş et

Artık 3D fare pozitesi bilindiğine göre, bu, lazeri ateşlemek için bir hedef pozisyonu olarak kullanılabilir. Oyuncunun silahı ile hedefler arasında bir ikinci ışın taraması Raycast fonksiyonu kullanılarak yapılabilir.

  1. Betiğin en üstüne MAX_LASER_DISTANCE adında bir sabit tanımlayın ve 500 değerine atayın veya lazer blaster için seçtiğiniz bir mesafeye göre ayarlayın.


    local UserInputService = game:GetService("UserInputService")
    local tool = script.Parent
    local MAX_MOUSE_DISTANCE = 1000
    local MAX_LASER_DISTANCE = 500
  2. getWorldMousePosition fonksiyonunun altında fireWeapon adında bir fonksiyon oluşturun.

  3. getWorldMousePosition'ı çağırın ve sonucunu mousePosition adlı bir değişkene atayın. Bu, ışın tarama için hedef pozisyon olacaktır.


    -- Hiçbir nesneye çarpılmadı, bu nedenle ışının sonundaki konumu hesapla
    return screenToWorldRay.Origin + directionVector
    end
    end
    local function fireWeapon()
    local mouseLocation = getWorldMousePosition()
    end
    local function toolEquipped()
    tool.Handle.Equip:Play()
    end

Bu sefer, ışın tarama fonksiyonu için yön vektörü, oyuncunun alet pozisyonundan hedef konuma giden yönü temsil edecektir.

  1. targetDirection adında bir değişken tanımlayın ve yön vektörünü mouseLocation'dan tool.Handle.Position'u çıkartarak hesaplayın.

  2. Vektörü Unit özelliğini kullanarak normalleştirin. Bu, 1 büyüklüğüne sahip olmasını sağlar, böylece daha sonra bir uzunluk ile çarpması kolay olur.


    local function fireWeapon()
    local mouseLocation = getWorldMousePosition()
    -- Normalleştirilmiş bir yön vektörü hesaplayın ve lazer mesafesi ile çarpın
    local targetDirection = (mouseLocation - tool.Handle.Position).Unit
    end
  3. directionVector adında bir değişken tanımlayıp bunu targetDirectionMAX_LASER_DISTANCE ile çarparak atayın.


    local targetDirection = (mouseLocation - tool.Handle.Position).Unit
    -- Silahın ateşlemek için yönü, maksimum mesafe ile çarpılmış
    local directionVector = targetDirection * MAX_LASER_DISTANCE
    end

RaycastParams nesnesi, ışın tarama fonksiyonu için ek parametreler saklamak için kullanılabilir. Bu, lazer blaster'ınızda ışının yanlışlıkla ateş eden oyuncuya çarpmadığından emin olmak için kullanılacaktır. FilterDescendantsInstances özelliğine dahil edilen herhangi bir parça, ışın taramada göz ardı edilecektir.

  1. fireWeapon fonksiyonuna devam edin ve weaponRaycastParams adlı bir değişken tanımlayın. Ona yeni bir RaycastParams nesnesi atayın.

  2. Oyuncunun yerel karakter'ine ait bir tablo oluşturun ve bunu weaponRaycastParams.FilterDescendantsInstances özelliğine atayın.

  3. Oyuncunun alet tutma pozisyona, directionVector'a doğru ışın taraması yapın. Bu sefer, weaponRaycastParams'ı bir argüman olarak eklemeyi unutmayın. Bunu weaponRaycastResult adlı bir değişkene atayın.


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()
-- Normalleştirilmiş bir yön vektörü hesaplayın ve lazer mesafesi ile çarpın
local targetDirection = (mouseLocation - tool.Handle.Position).Unit
-- Silahın ateşlemek için yönü, maksimum mesafe ile çarpılmış
local directionVector = targetDirection * MAX_LASER_DISTANCE
-- Oyuncunun karakterini göz ardı edin
local weaponRaycastParams = RaycastParams.new()
weaponRaycastParams.FilterDescendantsInstances = {Players.LocalPlayer.Character}
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)
end

Son olarak, ışın tarama işleminin bir değer döndürdüğünü kontrol etmeniz gerekir. Eğer bir değer döndürülmüşse, bir nesne ışın tarafından vurulmuş demektir ve silah ile vurulma konumu arasında lazer oluşturulabilir. Eğer hiçbir şey döndürülmezse, lazeri oluşturmak için son pozisyon hesaplanmalıdır.

  1. Boş bir hitPosition adlı değişken tanımlayın.

  2. weaponRaycastResult değer döndüğü için bir if deyimi kullanın. Eğer bir nesneye vurulduysa hitPosition'a weaponRaycastResult.Position atayın.


    local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)
    -- Başlangıç ve bitiş pozisyonları arasında herhangi bir nesneye vurulup vurulmadığını kontrol et
    local hitPosition
    if weaponRaycastResult then
    hitPosition = weaponRaycastResult.Position
    end
  3. Eğer weaponRaycastResult değere sahip değilse, lazerin son pozisyonunu hesaplamak için alet tutma pozisyonunu hitPosition'a atayın ve directionVector ile toplamanız gerekir.


    local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)
    -- Başlangıç ve bitiş pozisyonları arasında herhangi bir nesneye vurulup vurulmadığını kontrol et
    local hitPosition
    if weaponRaycastResult then
    hitPosition = weaponRaycastResult.Position
    else
    -- Maksimum lazer mesafesine göre son pozisyonu hesapla
    hitPosition = tool.Handle.Position + directionVector
    end
    end
  4. toolActivated fonksiyonuna gidin ve silah her etkinleştirildiğinde lazerin ateşlenmesi için fireWeapon fonksiyonunu çağırın.


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

Vurulan nesneyi kontrol et

Lazerin vurduğu nesnenin bir oyuncunun karakterinin parçası mı yoksa sadece bir ortam parçası mı olduğunu bulmak için, her karakterin bir Humanoid taşıdığını kontrol etmek gerekir.

Öncelikle karakter modelini bulmalısınız. Eğer bir karakterin bir parçasına vurulduysa, vurulan nesnenin üst öğesinin karakter olduğuna güvenemezsiniz. Lazerin bir vücut parçasına, aksesuarına veya araca çarpması mümkündür; bunların hepsi karakterin hiyerarşisinde farklı parçalardadır.

Eğer varsa, lazerin vurduğu nesnenin bir karakter modelinin atası olup olmadığını bulmak için FindFirstAncestorOfClass metodunu kullanabilirsiniz. Eğer bir model bulursanız ve bunun içinde bir humanoid varsa, büyük ihtimalle bu bir karakterdir.

  1. weaponRaycastResult if durumuna aşağıdaki belirlenen kodu ekleyerek bir karakterin vurulup vurulmadığını kontrol edin.


    -- Başlangıç ve bitiş pozisyonları arasında herhangi bir nesneye vurulup vurulmadığını kontrol et
    local hitPosition
    if weaponRaycastResult then
    hitPosition = weaponRaycastResult.Position
    -- Vurulan örnek bir karakter modelinin çocuğu olacaktır
    -- Eğer model içinde bir humanoid bulunursa, büyük ihtimalle bu bir oyuncunun karakteridir
    local characterModel = weaponRaycastResult.Instance:FindFirstAncestorOfClass("Model")
    if characterModel then
    local humanoid = characterModel:FindFirstChildWhichIsA("Humanoid")
    if humanoid then
    print("Oyuncuya vuruldu")
    end
    end
    else
    -- Maksimum lazer mesafesine göre son pozisyonu hesapla
    hitPosition = tool.Handle.Position + directionVector
    end

Artık lazer blaster, ışın tarama işlemi başka bir oyuncuya çarptığında Çıktı penceresine Oyuncuya vuruldu yazdıracaktır.

Birden fazla oyuncu ile test et

Silahın lazerin diğer oyuncuları bulup bulamadığını test etmek için iki oyuncuya ihtiyacınız var, bu yüzden yerel bir sunucu başlatmanız gerekir. Talimatlar için çoklu istemci simülasyonu sayfasına bakın.

Bir istemcide, başka bir oyuncuyu vurarak silahı test edin. Her seferinde çıktıda Oyuncuya vuruldu görünmesi gerekir.

Lazer konumunu bul

Blaster, hedefine kırmızı bir ışın göndermelidir. Bunun için fonksiyon bir ModuleScript içinde bulunmalıdır, böylece gelecekte diğer betiklerde yeniden kullanılabilir. Öncelikle, betik lazer ışınının hangi pozisyonda render edilmesi gerektiğini bulması gerekir.

  1. LaserRenderer adında bir ModuleScript oluşturun ve bunu StarterPlayer altında StarterPlayerScripts'e ebeveyn yapın.

  2. Betiği açın ve modül tablosunun adını betiğin adı olan LaserRenderer olarak değiştirin.

  3. SHOT_DURATION adında bir değişken tanımlayın ve 0.15 değerine atayın. Bu, lazerin görünür olacağı süreyi (saniye olarak) ifade eder.

  4. toolHandle ve endPosition adında iki parametre ile LaserRenderer içinde createLaser adında bir fonksiyon oluşturun.


    local LaserRenderer = {}
    local SHOT_DURATION = 0.15 -- Lazerin görünür olduğu süre
    -- Başlangıç pozisyondan son pozisyona kadar bir lazer ışını oluştur
    function LaserRenderer.createLaser(toolHandle, endPosition)
    end
    return LaserRenderer
  5. startPosition adıyla bir değişken tanımlayın ve toolHandle'nin Position özelliğini onun değeri olarak ayarlayın. Bu, oyuncunun lazer blaster'ının konumu olacaktır.

  6. laserDistance adında bir değişken tanımlayın ve startPosition ile endPosition arasındaki farkı bulmak için endPosition'dan startPosition'ı çıkartın. Bunun uzunluğunu Magnitude özelliğini kullanarak elde edin.


    function LaserRenderer.createLaser(toolHandle, endPosition)
    local startPosition = toolHandle.Position
    local laserDistance = (startPosition - endPosition).Magnitude
    end
  7. laserCFrame adında bir değişken tanımlayın ve lazer ışınının konumunu ve yönelimini saklayın. Konum, ışının başlangıç ve sonunun ortasında yer almalıdır. CFrame.lookAt kullanarak startPosition'da ve endPosition'a bakacak şekilde yeni bir CFrame oluşturun. Bunun üzerine ışının ortasında negatif laserDistance değerinin yarısı ile çarpın.


    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

Lazer parçasını oluştur

Artık bir lazer ışını oluşturmanın yerini bildiğinize göre, ışının kendisini eklemeniz gerekir. Bunu Neon bir parça ile kolayca yapabilirsiniz.

  1. laserPart adında bir değişken tanımlayın ve ona yeni bir Part örneği atayın.

  2. laserPart'ın aşağıdaki özelliklerini ayarlayın:

    1. Size: Vector3.new(0.2, 0.2, laserDistance)
    2. CFrame: laserCFrame
    3. Anchored: true
    4. CanCollide: false
    5. Color: Color3.fromRGB(225, 0, 0) (açık kırmızı renk)
    6. Material: Enum.Material.Neon
  3. laserPartWorkspace'e ebeveyn yapın.

  4. Betiği Class.Debris hizmetine ekleyerek, SHOT_DURATION değişkeninde belirtilen süreden sonra kaldırılmasını sağlayın.


    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
    -- Lazer ışınını süre sonunda kaldırılması için Debris hizmetine ekleyin
    Debris:AddItem(laserPart, SHOT_DURATION)
    end

Artık lazer ışınını render etmek için fonksiyon tamamlandı, bu ToolController tarafından çağrılabilir.

  1. ToolController betiğinin en üstünde LaserRenderer adında bir değişken tanımlayın ve PlayerScripts'de bulunan LaserRenderer ModuleScript'ini çağırın.


    local UserInputService = game:GetService("UserInputService")
    local Players = game:GetService("Players")
    local LaserRenderer = require(Players.LocalPlayer.PlayerScripts.LaserRenderer)
    local tool = script.Parent
  2. fireWeapon fonksiyonunun sonunda, LaserRenderer'ın createLaser fonksiyonunu çağırarak alet tutma ve hitPosition'u argüman olarak verin.


    -- Maksimum lazer mesafesine göre son pozisyonu hesapla
    hitPosition = tool.Handle.Position + directionVector
    end
    LaserRenderer.createLaser(tool.Handle, hitPosition)
    end
  3. Oyunu başlatarak silahı test edin. Silah etkinleştirildiğinde, lazer ışını alet ile fare arasında görünür olmalıdır.

Silah ateşleme hızını kontrol et

Silahların her bir atış arasında bir bekleme süresine ihtiyacı vardır, böylece oyuncular kısa bir süre içinde aşırı hasar veremezler. Bu, bir oyuncunun en son ateş ettiği zamandan ne kadar zaman geçtiğini kontrol ederek kontrol edilebilir.

  1. ToolController'ın en üstünde FIRE_RATE adında bir değişken tanımlayın. Bu, her atış arasındaki en kısa süreyi belirleyecektir. Dilediğiniz bir değeri verin; bu örnek 0.3 saniye kullanmaktadır.

  2. Hemen altına timeOfPreviousShot adında bir başka değişken tanımlayın ve değerini 0 olarak atayın. Bu, oyuncunun en son ateş ettiği zamanı saklayacak ve her atışta güncellenecektir.


    local MAX_MOUSE_DISTANCE = 1000
    local MAX_LASER_DISTANCE = 300
    local FIRE_RATE = 0.3
    local timeOfPreviousShot = 0
  3. Parametre almayacak şekilde canShootWeapon adında bir fonksiyon oluşturun. Bu fonksiyon, bir önceki atıştan ne kadar zaman geçtiğini kontrol edecek ve doğru veya yanlış döndürecektir.


    local FIRE_RATE = 0.3
    local timeOfPreviousShot = 0
    -- Önceki atıştan yeterince zaman geçip geçmediğini kontrol et
    local function canShootWeapon()
    end
    local function getWorldMousePosition()
  4. Fonksiyonun içinde currentTime adında bir değişken tanımlayın; ona tick() fonksiyonunun sonucunu atayın. Bu, 1 Ocak 1970'den (yaygın olarak zaman hesaplamaları yapmak için kullanılan bir tarih) itibaren kaç saniye geçtiğini döndürür.

  5. currentTime'dan timeOfPreviousShot'ı çıkarın ve sonuç FIRE_RATE'ten küçükse false döndürün; aksi takdirde true döndürün.


    -- Önceki atıştan yeterince zaman geçip geçmediğini kontrol et
    local function canShootWeapon()
    local currentTime = tick()
    if currentTime - timeOfPreviousShot < FIRE_RATE then
    return false
    end
    return true
    end
  6. fireWeapon fonksiyonunun sonunda, silah her ateşlendiğinde timeOfPreviousShottick ile güncelleyin.


    hitPosition = tool.Handle.Position + directionVector
    end
    timeOfPreviousShot = tick()
    LaserRenderer.createLaser(tool.Handle, hitPosition)
    end
  7. toolActivated fonksiyonunun içinde, silahın ateşlenip ateşlenemeyeceğini kontrol etmek için bir if durumu oluşturun ve canShootWeapon'ı çağırın.


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

Blaster'ı test ettiğinizde, ne kadar hızlı tıklarsanız tıklayın, her atış arasında her zaman kısa bir 0.3 saniye gecikme olmalıdır.

Oyuncuyu hasar verme

İstemciler, diğer istemcilere doğrudan hasar veremez; sunucu, bir oyuncunun vurulması durumunda hasarı vermekle sorumludur.

İstemciler, bir karakterin vurulduğunu sunucuya iletmek için bir RemoteEvent kullanabilir. Bunlar ReplicatedStorage içinde saklanmalıdır, böylece hem istemciye hem de sunucuya görünür.

  1. ReplicatedStorage içinde Events adında bir Klasör oluşturun.

  2. Events klasörüne bir RemoteEvent ekleyin ve adını DamageCharacter koyun.

  3. ToolController içinde, betiğin başında ReplicatedStorage ve Events klasörü için değişkenler oluşturun.


    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. fireWeapon fonksiyonundaki "Oyuncuya vuruldu" yazdırma komutunu, DamageCharacter uzaktan olayıyla characterModel değişkenini argüman olarak ateşleyecek şekilde değiştirin.


    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
    -- Maksimum lazer mesafesine göre son pozisyonu hesapla
    hitPosition = tool.Handle.Position + directionVector
    end

Şimdi sunucu, uzaktan çağırma olayı tetiklendiğinde, vurulan oyuncuya hasar vermelidir.

  1. ServerScriptService içine bir Script ekleyin ve adını ServerLaserManager olarak koyun.

  2. LASER_DAMAGE adında bir değişken tanımlayın ve değerini 10 olarak ayarlayın veya seçtiğiniz başka bir değeri belirleyin.

  3. playerFired ve characterToDamage adında iki parametreye sahip damageCharacter adlı bir fonksiyon oluşturun.

  4. Fonksiyon içinde karakterin Humanoid'ini bulun ve sağlığından LASER_DAMAGE'ı çıkarın.

  5. DamageCharacter uzaktan olayına damageCharacter fonksiyonunu bağlayın.


    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
    -- Karakterin sağlığını azalt
    humanoid.Health -= LASER_DAMAGE
    end
    end
    -- Olayları uygun fonksiyonlara bağlayın
    eventsFolder.DamageCharacter.OnServerEvent:Connect(damageCharacter)
  6. İki oyuncu ile blaster'ı test etmek için yerel sunucuyu başlatın. Diğer oyuncuya ateş ettiğinizde, sağlıkları LASER_DAMAGE'da belirtilen miktarda azalma gösterecektir.

Diğer oyuncuların lazer ışınlarını render et

Şu anda, lazer ışını, projeyi fırlatan istemcide oluşturulur; bu yüzden yalnızca onu görebilirler.

Eğer lazer ışını sunucuda oluşturulursa, herkes bunu görebilir. Ancak, istemcinin silahı ateşlemesi ile sunucunun ateşli kızın bilgisini alması arasında küçük bir gecikme olacaktir. Bu, silahı ateşlemekte olan istemcinin, silahı etkinleştirdiği zaman ile lazer ışınını görünce arasındaki gecikmeyi deneyimlemesine neden olur; bu durum silahın gecikmeli hissettirilmesine neden olur.

Bu sorunu çözmek için, her istemci kendi lazer ışınlarını oluşturmaktadır. Bu, silahı ateşleyen istemcinin lazer ışınını anında görmesini sağlar. Diğer istemciler, başka bir oyuncunun ateş etmesi ile lazerin görünmeye başlaması arasında küçük bir gecikme yaşayacaklardır. En iyi senaryo budur: Bir istemcinin lazerini diğer istemcilere daha hızlı bir şekilde iletmenin bir yolu yoktur.

Ateş eden istemci

Öncelikle istemcinin, bir lazer ateşlediğini sunucuya bildirmesi ve son pozisyonu göndermesi gerekir.

  1. ReplicatedStorage'deki Events klasörüne bir RemoteEvent ekleyin ve adını LaserFired koyun.

  2. ToolController betiğindeki fireWeapon fonksiyonunu bulun. Fonksiyonun sonunda LaserFired uzaktan olayını göndererek hitPosition'ı argüman olarak kullanın.


    hitPosition = tool.Handle.Position + directionVector
    end
    timeOfPreviousShot = tick()
    eventsFolder.LaserFired:FireServer(hitPosition)
    LaserRenderer.createLaser(tool.Handle, hitPosition)
    end

Sunucu

Artık sunucu, istemcinin ateş ettiğini bildiren olayı almalı ve tüm istemcilere lazer ışınının başlangıç ve bitiş pozisyonunu söylemelidir, böylece onlar da ışını render edebilirler.

  1. ServerLaserManager betiğinde, iki parametreye sahip playerFiredLaser adında bir fonksiyon oluşturun؛ playerFired ve endPosition adında.

  2. Fonksiyonu LaserFired uzaktan olayına bağlayın.


    -- Tüm istemcilere lazerin ateşlendiğini bildir, böylece lazeri görüntüleyebilirler
    local function playerFiredLaser(playerFired, endPosition)
    end

    -- Olayları uygun fonksiyonlara bağlayın
    eventsFolder.DamageCharacter.OnServerEvent:Connect(damageCharacter)
    eventsFolder.LaserFired.OnServerEvent:Connect(playerFiredLaser)

Sunucunun lazerin başlangıç pozisyonunu bilmesi gerekir. Bu istemciden gönderilebilir, fakat istemciden mümkün olduğunca güvenmemek daha iyidir. Karakterin silah tutma pozisyonu, lazerin başlangıç pozisyonu olacağından, sunucu bunu oradan bulabilir.

  1. playerFiredLaser fonksiyonunun üstüne, player parametresi ile birlikte getPlayerToolHandle adını veren bir fonksiyon oluşturun.

  2. İşte aşağıdaki kodu kullanarak oyuncunun karakterinde arama yaparak silahın tutma nesnesini bulun ve geri döndürün:


    local LASER_DAMAGE = 10
    -- Oyuncunun tuttuğu aracın tutma yerini bulun
    local function getPlayerToolHandle(player)
    local weapon = player.Character:FindFirstChildOfClass("Tool")
    if weapon then
    return weapon:FindFirstChild("Handle")
    end
    end
    -- Tüm istemcilere lazerin ateşlendiğini bildir, böylece lazeri görüntüleyebilirler
    local function playerFiredLaser(playerFired, endPosition)

Artık sunucu, FireAllClients'ı çağırarak LaserFired uzaktan olayını göndererek istemcilere lazeri render etmek için gerekli bilgileri (lazer ateşleyen playerı, toolHandle'ı ve endPosition'ı) gönderebilir.

  1. playerFiredLaser fonksiyonunda, getPlayerToolHandle fonksiyonunu playerFired ile çağırın ve değerini toolHandle adında bir değişkene atayın.

  2. Eğer toolHandle varsa, tüm istemcileri playerFired, toolHandle ve endPosition argümanlarıyla LaserFired olayını yayınlayın.


    -- Tüm istemcilere lazerin ateşlendiğini bildir, böylece lazeri görüntüleyebilirler
    local function playerFiredLaser(playerFired, endPosition)
    local toolHandle = getPlayerToolHandle(playerFired)
    if toolHandle then
    eventsFolder.LaserFired:FireAllClients(playerFired, toolHandle, endPosition)
    end
    end

İstemcilerde render et

Artık FireAllClients çağrıldı; her istemci sunucudan, lazerin ışınını render etmek için bir olay alır. Her istemci, sunucudan gönderilen toolHandle ve endPosition değerini kullanarak lazer ışınını render edebilir. Lazer ışınını en başında ateşleyen oyuncu, bu olayı göz ardı etmelidir; aksi halde iki lazer görecektir.

  1. StarterPlayerScripts içinde ClientLaserManager adında bir LocalScript oluşturun.

  2. Betik içinde LaserRenderer modülünü çağırın.

  3. playerWhoShot, toolHandle ve endPosition parametreleri ile createPlayerLaser adında bir fonksiyon oluşturun.

  4. Bu fonksiyonu Events klasöründeki LaserFired uzaktan olayı ile bağlayın.

  5. Fonksiyonda bir if durumu kullanarak playerWhoShot'ın LocalPlayer'a eşit olmayan durumunu kontrol edin.

  6. If arasındaki durum içerisinde, toolHandle ve endPosition argümanlarıyla LaserRenderer modülünden createLaser fonksiyonunu çağırın.


    local Players = game:GetService("Players")
    local ReplicatedStorage = game:GetService("ReplicatedStorage")
    local LaserRenderer = require(script.Parent:WaitForChild("LaserRenderer"))
    local eventsFolder = ReplicatedStorage.Events
    -- Başka bir oyuncunun lazerini görüntüle
    local function createPlayerLaser(playerWhoShot, toolHandle, endPosition)
    if playerWhoShot ~= Players.LocalPlayer then
    LaserRenderer.createLaser(toolHandle, endPosition)
    end
    end
    eventsFolder.LaserFired.OnClientEvent:Connect(createPlayerLaser)
  7. Blaster'ı 2 oyuncu ile test edin; yerel sunucuyu başlatın. Her istemciyi ekranınızın farklı tarafına yerleştirin, böylece her iki pencerede görünür hale gelsin. Bir istemcide ateş ettiğinizde, diğer istemcide lazeri göreceksiniz.

Ses efektleri

Şu anda, ateş etme ses efekti yalnızca mermiyi ateşleyen istemcide çalmaktadır. Diğer oyuncuların da bunu duyması için, sesi çalan kodu taşımamız gerekir.

  1. ToolController betiğinde toolActivated fonksiyonuna gidin ve etkinleştir sesini oynatan satırı kaldırın.


    local function toolActivated()
    if canShootWeapon() then
    fireWeapon()
    end
    end
  2. LaserRenderer'daki createLaser fonksiyonunun sonunda, shootingSound adında bir değişken tanımlayın ve toolHandle'nin Activate sesini kontrol etmek için FindFirstChild() metodunu kullanın.

  3. Eğer shootingSound var ise, Play() fonksiyonunu çağırın.


    laserPart.Parent = workspace
    -- Lazer ışınının Debris hizmetine eklenmesini sağlamak için
    Debris:AddItem(laserPart, SHOT_DURATION)
    -- Silahın ateşleme sesini çal
    local shootingSound = toolHandle:FindFirstChild("Activate")
    if shootingSound then
    shootingSound:Play()
    end
    end

Uzaktan olayları güvenlik doğrulaması

Eğer sunucu gelen isteklerden verileri kontrol etmiyorsa, bir hacker, uzaktan işlevleri ve olayları kötüye kullanabilir ve onlara sunucuya sahte değerler göndermek için kullanabilir. Bu nedenle, bunu önlemek için sunucu tarafı doğrulama kullanmak önemlidir.

Mevcut olan DamageCharacter uzaktan olayı, saldırıya oldukça açıktır. Hackler bu olayı kullanaraktan, kendilerini vurmadan istedikleri herhangi bir oyuncuya hasar verebilir.

Doğrulama, sunucuya gönderilen değerlerin gerçekçi olup olmadığını kontrol etme sürecidir. Bu durumda, sunucunun:

  • Oyuncu ile lazerin vurduğu pozisyon arasındaki mesafenin belirli bir sınır içinde olup olmadığını kontrol etmesi gerekir.
  • Ateşlenen silah ile vurulan pozisyon arasında ışın tarama yaparak, atışın mümkün olup olmadığını ve herhangi bir duvardan geçip geçmediğini kontrol etmesi gerekir.

İstemci

İstemci artık sunucuya vurulan pozisyonu göndererek mesafenin gerçekçi olup olmadığını kontrol etmelidir.

  1. ToolController içinde, fireWeapon fonksiyonundaki DamageCharacter uzaktan olayının ateşlendiği satıra gidin.

  2. hitPosition'ı argüman olarak ekleyin.


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

Sunucu

İstemci artık DamageCharacter uzaktan olayında, bir ek parametre gönderdiğinden, ServerLaserManager'ın bunu kabul edecek şekilde ayarlanması gerekir.

  1. ServerLaserManager betiğinde, damageCharacter fonksiyonuna hitPosition parametresini ekleyin.


    function damageCharacter(playerFired, characterToDamage, hitPosition)
    local humanoid = characterToDamage:FindFirstChildWhichIsA("Humanoid")
    if humanoid then
    -- Karakterin sağlığını azalt
    humanoid.Health -= LASER_DAMAGE
    end
    end
  2. getPlayerToolHandle fonksiyonunun altına, üç parametre alan isHitValid adında bir fonksiyon oluşturun; playerFired, characterToDamage ve hitPosition.


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

İlk kontrol, vurulan pozisyon ile vurulan karakter arasındaki mesafenin kontrolüdür.

  1. Betiğin başında MAX_HIT_PROXIMITY adında bir değişken tanımlayın ve değerini 10 olarak atayın. Bu, vurulan konum ile karakter arasındaki en fazla mesafeyi belirleyecektir. Bir tolerans gereklidir, çünkü karakter, istemci ateş ettiğinden beri biraz hareket etmiş olabilir.


    local ReplicatedStorage = game:GetService("ReplicatedStorage")
    local eventsFolder = ReplicatedStorage.Events
    local LASER_DAMAGE = 10
    local MAX_HIT_PROXIMITY = 10
  2. isHitValid fonksiyonunda, karakter ile vurulan pozisyon arasındaki mesafeyi hesaplayın. Eğer mesafe MAX_HIT_PROXIMITY değerinden daha büyükse false döndürün.


    local function isHitValid(playerFired, characterToDamage, hitPosition)
    -- Vurulan karakter ile vurulan pozisyon arasındaki mesafeyi doğrula
    local characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitude
    if characterHitProximity > MAX_HIT_PROXIMITY then
    return false
    end
    end

İkinci kontrol, ateş edip vurulan pozisyon arasındaki ışın taramasını içerir. Eğer ışın taraması, karakter olmayan bir nesneyi döndürürse, atış geçerli değildir; çünkü bir şey atışı engellemiştir.

  1. Aşağıdaki kodu kopyalayarak bu kontrolü gerçekleştirin. Fonksiyonun sonunda true döndürün; eğer sona ulaşırsa, tüm kontroller geçmiş demektir.


    local function isHitValid(playerFired, characterToDamage, hitPosition)
    -- Vurulan karakter ile vurulan pozisyon arasındaki mesafeyi doğrula
    local characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitude
    if characterHitProximity > 10 then
    return false
    end
    -- Duvarlardan geçip geçmediğini kontrol et
    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)
    -- Eğer vurulan nesne, karakter değilse, atışı göz ardı et
    if rayResult and not rayResult.Instance:IsDescendantOf(characterToDamage) then
    return false
    end
    end
    return true
    end
  2. damageCharacter fonksiyonunda validShot adında bir değişken tanımlayın. Değerini, isHitValid fonksiyonunu playerFired, characterToDamage ve hitPosition argümanlarıyla çağırdığınızda alın.

  3. Aşağıdaki if durumuna ek olarak validShot'in true olduğunu kontrol etmek için bir and operatörü ekleyin.


    function damageCharacter(playerFired, characterToDamage, hitPosition)
    local humanoid = characterToDamage:FindFirstChildWhichIsA("Humanoid")
    local validShot = isHitValid(playerFired, characterToDamage, hitPosition)
    if humanoid and validShot then
    -- Karakterin sağlığını azalt
    humanoid.Health -= LASER_DAMAGE
    end
    end

Artık damageCharacter uzaktan olayı daha güvenli hale geldi ve çoğu oyuncunun bunu kötüye kullanmasını engelleyecektir. Ancak bazı kötü niyetli oyuncular, doğrulamayı aşmanın bir yolunu bulabilir; uzaktan olayları güvenli hale getirmek sürekli bir çaba gerektirir.

Lazer blaster'ınız artık tamamlandı ve temel bir vurma tespiti sistemi ile birlikte çalışıyor. Kullanıcı Girdisini Tespit Etme öğretisini deneyin, böylece lazer blaster'ınıza bir yeniden doldurma eylemi ekleyebilirsiniz veya eğlenceli bir harita oluşturun ve lazer blaster'ınızı diğer oyuncular ile deneyin!

Nihai kod

ToolController


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
-- Önceki atıştan yeterince zaman geçip geçmediğini kontrol et
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()
-- 2D fare konumundan bir ışın oluştur
local screenToWorldRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
-- Işının birim yön vektörü, maksimum mesafe ile çarpılmış
local directionVector = screenToWorldRay.Direction * MAX_MOUSE_DISTANCE
-- Işının orijinal yönüne doğru ışın tarama yap
local raycastResult = workspace:Raycast(screenToWorldRay.Origin, directionVector)
if raycastResult then
-- Kesiti döndür
return raycastResult.Position
else
-- Hiçbir nesneye çarpılmadı, bu nedenle ışının sonundaki konumu hesapla
return screenToWorldRay.Origin + directionVector
end
end
local function fireWeapon()
local mouseLocation = getWorldMousePosition()
-- Normalleştirilmiş bir yön vektörü hesaplayın ve lazer mesafesi ile çarpın
local targetDirection = (mouseLocation - tool.Handle.Position).Unit
-- Silahın ateşlemek için yönü, maksimum mesafe ile çarpılmış
local directionVector = targetDirection * MAX_LASER_DISTANCE
-- Oyuncunun karakterini göz ardı edin
local weaponRaycastParams = RaycastParams.new()
weaponRaycastParams.FilterDescendantsInstances = {Players.LocalPlayer.Character}
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)
-- Başlangıç ve bitiş pozisyonları arasında herhangi bir nesneye vurulup vurulmadığını kontrol et
local hitPosition
if weaponRaycastResult then
hitPosition = weaponRaycastResult.Position
-- Vurulan örnek bir karakter modelinin çocuğu olacaktır
-- Eğer model içinde bir humanoid bulunursa, büyük ihtimalle bu bir oyuncunun karakteridir
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
-- Maksimum lazer mesafesine göre son pozisyonu hesapla
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)

LaserRenderer


local LaserRenderer = {}
local Debris = game:GetService("Debris")
local SHOT_DURATION = 0.15 -- Lazerin görünür olduğu süre
-- Başlangıç pozisyondan son pozisyona kadar bir lazer ışını oluştur
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
-- Lazer ışınının Debris hizmetine eklenmesini sağlamak için
Debris:AddItem(laserPart, SHOT_DURATION)
-- Silahın ateşleme sesini çal
local shootingSound = toolHandle:FindFirstChild("Activate")
if shootingSound then
shootingSound:Play()
end
end
return LaserRenderer

ServerLaserManager


local ReplicatedStorage = game:GetService("ReplicatedStorage")
local eventsFolder = ReplicatedStorage.Events
local LASER_DAMAGE = 10
local MAX_HIT_PROXIMITY = 10
-- Oyuncunun tuttuğu aracın tutma yerini bulun
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)
-- Vurulan karakter ile vurulan pozisyon arasındaki mesafeyi doğrula
local characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitude
if characterHitProximity > MAX_HIT_PROXIMITY then
return false
end
-- Duvarlardan geçip geçmediğini kontrol et
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)
-- Eğer vurulan nesne, karakter değilse, atışı göz ardı et
if rayResult and not rayResult.Instance:IsDescendantOf(characterToDamage) then
return false
end
end
return true
end
-- Tüm istemcilere lazerin ateşlendiğini bildir, böylece lazeri görüntüleyebilirler
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
-- Karakterin sağlığını azalt
humanoid.Health -= LASER_DAMAGE
end
end
-- Olayları uygun fonksiyonlara bağlayın
eventsFolder.DamageCharacter.OnServerEvent:Connect(damageCharacter)
eventsFolder.LaserFired.OnServerEvent:Connect(playerFiredLaser)

ClientLaserManager


local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local LaserRenderer = require(Players.LocalPlayer.PlayerScripts:WaitForChild("LaserRenderer"))
local eventsFolder = ReplicatedStorage.Events
-- Başka bir oyuncunun lazerini görüntüle
local function createPlayerLaser(playerWhoShot, toolHandle, endPosition)
if playerWhoShot ~= Players.LocalPlayer then
LaserRenderer.createLaser(toolHandle, endPosition)
end
end
eventsFolder.LaserFired.OnClientEvent:Connect(createPlayerLaser)
©2026 Roblox Corporation. Roblox, the Roblox logo and Powering Imagination are among our registered and unregistered trademarks in the U.S. and other countries.