Bu eğitimde, Oyuncu Araçları Oluştur bölümündeki blasterdan lazer nasıl atılacağını ve bunun bir oyuncuya çarpıp çarpmadığını nasıl tespit edeceğinizi öğreneceksiniz.
Çarpışmaları bulmak için Raycasting
Raycasting, bir başlangıç pozisyonundan belirli bir yön ve tanımlı bir uzunlukla görünmez bir ışın oluşturur. Eğer ışın, yolundaki nesneler veya arazi ile çarpışırsa, çarpışma hakkında konum ve çarpıştığı nesne gibi bilgiler döner.

Fare konumunu bulma
Bir lazer atılmadan önce, oyuncunun nereye nişan aldığını bilmeniz gerekir. Bu, oyuncunun ekran üzerindeki 2D fare konumundan, kameradan 3D dünyaya doğru raycasting yaparak bulunabilir. Işın, oyuncunun fare ile nişan aldığı her şeye çarpacaktır.
Oyuncu Araçları Oluştur bölümündeki Blaster aracının içindeki ToolController scriptini açın. Eğer bu eğitimi henüz tamamlamadıysanız, Blaster modelini indirebilir ve StarterPack'e ekleyebilirsiniz.

Scriptin en üstünde, değeri 1000 olan MAX_MOUSE_DISTANCE adında bir sabit tanımlayın.
getWorldMousePosition adında bir fonksiyon oluşturun.
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-- Olayları uygun fonksiyonlara bağlayıntool.Equipped:Connect(toolEquipped)tool.Activated:Connect(toolActivated)UserInputService'in GetMouseLocation fonksiyonunu kullanarak oyuncunun ekran üzerindeki 2D fare konumunu alın. Bunu mouseLocation adında bir değişkene atayın.
local UserInputService = game:GetService("UserInputService")local tool = script.Parentlocal MAX_MOUSE_DISTANCE = 1000local function getWorldMousePosition()local mouseLocation = UserInputService:GetMouseLocation()end
Artık 2D fare konumu biliniyor, X ve Y özellikleri Camera:ViewportPointToRay() fonksiyonu için parametre olarak kullanılabilir; bu fonksiyon ekranın 3D dünyaya bir Ray oluşturur.
mouseLocation'ın X ve Y özelliklerini Camera:ViewportPointToRay() fonksiyonu için argüman olarak kullanın. Bunu screenToWorldRay adında bir değişkene atayın.
local function getWorldMousePosition()local mouseLocation = UserInputService:GetMouseLocation()-- 2D fare konumundan bir ışın oluşturlocal 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ı. Bu, bir başlangıç pozisyonu ve 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ın MAX_MOUSE_DISTANCE kadar uzun olması gerektiğinden, yön vektörünü MAX_MOUSE_DISTANCE ile çarpmanız gerekecek.
directionVector adında bir değişken tanımlayın ve screenToWorldRay.Direction değerini MAX_MOUSE_DISTANCE ile çarparak atayın.
local function getWorldMousePosition()local mouseLocation = UserInputService:GetMouseLocation()-- 2D fare konumundan bir ışın oluşturlocal 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_DISTANCEworkspace'in WorldRoot:Raycast() fonksiyonunu çağırın, screenToWorldRay'in Origin özelliğini ilk argüman olarak ve directionVector'ı ikinci argüman olarak geçirin. Bunu raycastResult adında bir değişkene atayın.
local function getWorldMousePosition()local mouseLocation = UserInputService:GetMouseLocation()-- 2D fare konumundan bir ışın oluşturlocal 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 başlangıç noktasından yönüne doğru raycast yaplocal raycastResult = workspace:Raycast(screenToWorldRay.Origin, directionVector)
Çarpışma bilgileri
Eğer raycast işlemi, ışın tarafından vurulan bir nesne bulursa, bu, ışın ile nesne arasındaki çarpışma hakkında bilgi içeren bir RaycastResult döner.
| RaycastResult Özelliği | Açıklama |
|---|---|
| Instance | Işının kesiştiği BasePart veya Terrain hücresi. |
| Position | Kesişmenin gerçekleştiği yer; genellikle bir parça veya arazi yüzeyinde doğrudan bir nokta. |
| Material | Çarpışma noktasındaki malzeme. |
| Normal | Kesişen yüzeyin normal vektörü. Bu, yüzeyin hangi yöne baktığını belirlemek için kullanılabilir. |
Position özelliği, farenin üzerinde durduğu nesnenin konumu olacaktır. Eğer fare, MAX_MOUSE_DISTANCE mesafesi içinde herhangi bir nesne üzerinde durmuyorsa, raycastResult nil olacaktır.
raycastResult'ın var olup olmadığını kontrol etmek için bir if ifadesi oluşturun.
Eğer raycastResult bir değere sahipse, Position özelliğini döndürün.
Eğer raycastResult nil ise, raycast'in sonunu bulun. Fare konumunun 3D pozisyonunu screenToWorldRay.Origin ile directionVector'ı toplayarak hesaplayı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 başlangıç noktasından yönüne doğru raycast yap
local raycastResult = workspace:Raycast(screenToWorldRay.Origin, directionVector)
if raycastResult then
-- Kesişme 3D noktasını döndür
return raycastResult.Position
else
-- Hiçbir nesne vurulmadı, bu yüzden rayin sonundaki konumu hesapla
return screenToWorldRay.Origin + directionVector
end
endHedefe ateş etme
Artık 3D fare pozisyonu biliniyor, bu pozisyon lazerin ateşleneceği hedef pozisyonu olarak kullanılabilir. Oyuncunun silahı ile hedef pozisyonu arasında ikinci bir ışın atılabilir.
Scriptin en üstünde MAX_LASER_DISTANCE adında bir sabit tanımlayın ve değerini 500 olarak atayın veya lazer blaster için seçtiğiniz bir menzil belirleyin.
local UserInputService = game:GetService("UserInputService")local tool = script.Parentlocal MAX_MOUSE_DISTANCE = 1000local MAX_LASER_DISTANCE = 500getWorldMousePosition fonksiyonunun altında fireWeapon adında bir fonksiyon oluşturun.
getWorldMousePosition'ı çağırın ve sonucu mousePosition adında bir değişkene atayın. Bu, raycast için hedef pozisyon olacaktır.
-- Hiçbir nesne vurulmadı, bu yüzden rayin sonundaki konumu hesaplareturn screenToWorldRay.Origin + directionVectorendendlocal function fireWeapon()local mouseLocation = getWorldMousePosition()endlocal function toolEquipped()tool.Handle.Equip:Play()end
Bu sefer, raycast fonksiyonu için yön vektörü, oyuncunun araç pozisyonundan hedef konumuna olan yönü temsil edecektir.
targetDirection adında bir değişken tanımlayın ve mouseLocation'dan tool.Handle.Position'ı çıkararak yön vektörünü hesaplayın.
Vektörü Unit özelliğini kullanarak normalize edin. Bu, ona 1'lik bir büyüklük verir, bu da daha sonra bir uzunluk ile çarpmayı kolaylaştırır.
local function fireWeapon()local mouseLocation = getWorldMousePosition()-- Normalleştirilmiş bir yön vektörü hesaplayın ve lazer mesafesi ile çarpınlocal targetDirection = (mouseLocation - tool.Handle.Position).UnitenddirectionVector adında bir değişken tanımlayın ve targetDirection'ı MAX_LASER_DISTANCE ile çarparak atayın.
local targetDirection = (mouseLocation - tool.Handle.Position).Unit-- Silahı ateşlemek için yön, maksimum mesafe ile çarpılmışlocal directionVector = targetDirection * MAX_LASER_DISTANCEend
RaycastParams nesnesi, raycast fonksiyonu için ek parametreler depolamak için kullanılabilir. Bu, lazer blaster'ınızda, raycast'in silahı ateşleyen oyuncuya çarpmasını önlemek için kullanılacaktır. FilterDescendantsInstances özelliğine dahil edilen herhangi bir parça, raycast'te göz ardı edilecektir.
fireWeapon fonksiyonunu devam ettirin ve weaponRaycastParams adında bir değişken tanımlayın. Ona yeni bir RaycastParams nesnesi atayın.
Oyuncunun yerel karakter'ini içeren bir tablo oluşturun ve bunu weaponRaycastParams.FilterDescendantsInstances özelliğine atayın.
Oyuncunun araç tutma pozisyonundan, directionVector yönüne doğru raycast yapın. Bu sefer weaponRaycastParams'ı bir argüman olarak eklemeyi unutmayın. Bunu weaponRaycastResult adında 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ı ateşlemek için yön, maksimum mesafe ile çarpılmış
local directionVector = targetDirection * MAX_LASER_DISTANCE
-- Oyuncunun karakterini göz ardı edin, böylece kendilerine zarar vermelerini önleyin
local weaponRaycastParams = RaycastParams.new()
weaponRaycastParams.FilterDescendantsInstances = {Players.LocalPlayer.Character}
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)
endSon olarak, raycast işleminin bir değer döndürüp döndürmediğini kontrol etmeniz gerekecek. Eğer bir değer dönerse, ışın bir nesneye çarpmıştır ve silah ile çarpma konumu arasında bir lazer oluşturulabilir. Eğer hiçbir şey döndürülmezse, lazeri oluşturmak için son pozisyon hesaplanmalıdır.
hitPosition adında boş bir değişken tanımlayın.
weaponRaycastResult'ın bir değeri olup olmadığını kontrol etmek için bir if ifadesi kullanın. Eğer bir nesne vurulduysa, weaponRaycastResult.Position'ı hitPosition'a atayın.
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)-- Başlangıç ve bitiş pozisyonu arasında herhangi bir nesne vurulup vurulmadığını kontrol etlocal hitPositionif weaponRaycastResult thenhitPosition = weaponRaycastResult.PositionendEğer weaponRaycastResult değeri yoksa, raycast'in son pozisyonunu, araç tutma pozisyonunun pozisyonu ile directionVector'ı toplayarak hesaplayın. Bunu hitPosition'a atayın.
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)-- Başlangıç ve bitiş pozisyonu arasında herhangi bir nesne vurulup vurulmadığını kontrol etlocal hitPositionif weaponRaycastResult thenhitPosition = weaponRaycastResult.Positionelse-- Maksimum lazer mesafesine göre son pozisyonu hesaplahitPosition = tool.Handle.Position + directionVectorendendtoolActivated fonksiyonuna gidin ve lazerin her seferinde ateşlenmesi için fireWeapon fonksiyonunu çağırın.
local function toolActivated()tool.Handle.Activate:Play()fireWeapon()end
Vurulan nesneyi kontrol etme
Lazerin vurduğu nesnenin bir oyuncunun karakterinin parçası mı yoksa sadece bir manzara parçası mı olduğunu bulmak için, her karakterin bir Humanoid içerdiğini kontrol etmeniz gerekecek.
Öncelikle karakter modelini bulmanız gerekecek. Eğer karakterin bir parçası vurulduysa, vurulan nesnenin ebeveyninin karakter olduğunu varsayamazsınız. Lazer, bir vücut parçasına, bir aksesuara veya bir araca çarpmış olabilir; bunların hepsi karakterin hiyerarşisinde farklı yerlerde bulunur.

Işının vurduğu nesnenin bir karakter modeli üst nesnesi olup olmadığını bulmak için FindFirstAncestorOfClass kullanabilirsiniz. Eğer bir model bulursanız ve bu model bir humanoid içeriyorsa, çoğu durumda bunun bir karakter olduğunu varsayabilirsiniz.
weaponRaycastResult if ifadesine aşağıdaki vurgulanan kodu ekleyin, böylece bir karakterin vurulup vurulmadığını kontrol edin.
-- Başlangıç ve bitiş pozisyonu arasında herhangi bir nesne vurulup vurulmadığını kontrol etlocal hitPositionif weaponRaycastResult thenhitPosition = weaponRaycastResult.Position-- Vurulan nesne bir karakter modelinin çocuğu olacaktır-- Eğer modelde bir humanoid bulunursa, muhtemelen bir oyuncunun karakteridirlocal characterModel = weaponRaycastResult.Instance:FindFirstAncestorOfClass("Model")if characterModel thenlocal humanoid = characterModel:FindFirstChildWhichIsA("Humanoid")if humanoid thenprint("Oyuncu vuruldu")endendelse-- Maksimum lazer mesafesine göre son pozisyonu hesaplahitPosition = tool.Handle.Position + directionVectorend
Artık lazer blaster, raycast işlemi her başka bir oyuncuyu vurduğunda Çıktı penceresine Oyuncu vuruldu yazdırmalıdır.
Birden fazla oyuncu ile test etme
Silah raycast'inin diğer oyuncuları bulup bulmadığını test etmek için iki oyuncuya ihtiyacınız var, bu yüzden yerel bir sunucu başlatmanız gerekiyor. Talimatlar için çoklu istemci simülasyonu bölümüne bakın.
Bir istemcide, diğer oyuncuya nişan alarak silahı ateş etmeyi test edin. Her seferinde "Oyuncu vuruldu" çıktısı görünmelidir.
Lazer pozisyonunu bulma
Blaster, hedefe kırmızı bir ışın ateşlemelidir. Bunun için fonksiyon, daha sonra diğer scriptlerde yeniden kullanılabilmesi için bir ModuleScript içinde olacaktır. Öncelikle, lazer ışınının nerede render edileceğini bulmanız gerekecek.
LaserRenderer adında bir ModuleScript oluşturun ve bunu StarterPlayer altında StarterPlayerScripts'e yerleştirin.

Scripti açın ve modül tablosunun adını scriptin adı olan LaserRenderer olarak değiştirin.
SHOT_DURATION adında bir değişken tanımlayın ve değerini 0.15 olarak atayın. Bu, lazerin görünür olacağı süre (saniye cinsinden) olacaktır.
toolHandle ve endPosition adında iki parametreye sahip createLaser adında bir LaserRenderer fonksiyonu oluşturun.
local LaserRenderer = {}local SHOT_DURATION = 0.15 -- Lazerin görünür olduğu süre-- Başlangıç pozisyonundan bir bitiş pozisyonuna lazer ışını oluşturfunction LaserRenderer.createLaser(toolHandle, endPosition)endreturn LaserRendererstartPosition adında bir değişken tanımlayın ve toolHandle'ın Position özelliğini değer olarak atayın. Bu, oyuncunun lazer blaster'ının pozisyonu olacaktır.
laserDistance adında bir değişken tanımlayın ve endPosition'dan startPosition'ı çıkararak iki vektör arasındaki farkı bulun. Bunun Magnitude özelliğini kullanarak lazer ışınının uzunluğunu elde edin.
function LaserRenderer.createLaser(toolHandle, endPosition)local startPosition = toolHandle.Positionlocal laserDistance = (startPosition - endPosition).MagnitudeendLazer ışınının pozisyonunu ve yönünü depolamak için bir laserCFrame değişkeni tanımlayın. Pozisyon, ışının başlangıç ve bitiş noktalarının ortası olmalıdır. CFrame.lookAt kullanarak startPosition'da bulunan ve endPosition'a bakan yeni bir CFrame oluşturun. Bunu, negatif laserDistance'in yarısı olan yeni bir CFrame ile çarparak ortayı elde edin.
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
Lazer parçasını oluşturma
Artık bir lazer ışını oluşturmak için nereye gideceğinizi biliyorsunuz, şimdi ışını kendisini eklemeniz gerekiyor. Bu, Neon bir parça ile kolayca yapılabilir.
laserPart adında bir değişken tanımlayın ve ona yeni bir Part örneği atayın.
laserPart'ın aşağıdaki özelliklerini ayarlayın:
- Boyut: Vector3.new(0.2, 0.2, laserDistance)
- CFrame: laserCFrame
- Sabit: true
- Çarpışma: false
- Renk: Color3.fromRGB(225, 0, 0) (güçlü bir kırmızı renk)
- Malzeme: Enum.Material.Neon
laserPart'ı Workspace'e ekleyin.
Parçayı Debris servisine ekleyin, böylece SHOT_DURATION değişkenindeki saniye sayısı kadar sonra kaldırılır.
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-- Lazer ışınını Debris servisine ekleyin, böylece kaldırılır ve temizlenirDebris:AddItem(laserPart, SHOT_DURATION)end
Artık lazer ışınını render etmek için fonksiyon tamamlandı, bu fonksiyon ToolController tarafından çağrılabilir.
ToolController scriptinin en üstünde, LaserRenderer adında bir değişken tanımlayın ve PlayerScripts'teki LaserRenderer ModuleScript'ini gereksin.
local UserInputService = game:GetService("UserInputService")local Players = game:GetService("Players")local LaserRenderer = require(Players.LocalPlayer.PlayerScripts.LaserRenderer)local tool = script.ParentfireWeapon fonksiyonunun sonunda, lazeri oluşturmak için LaserRenderer createLaser fonksiyonunu çağırın, argüman olarak araç tutma ve hitPosition'ı kullanın.
-- Maksimum lazer mesafesine göre son pozisyonu hesaplahitPosition = tool.Handle.Position + directionVectorendLaserRenderer.createLaser(tool.Handle, hitPosition)endOyuncunun silahı ateşleyerek lazerin görünür olup olmadığını test edin. Araç etkinleştirildiğinde, silah ile fare arasında bir lazer ışını görünmelidir.
Silah ateşleme hızını kontrol etme
Silahların, oyuncuların kısa bir süre içinde çok fazla hasar vermesini önlemek için her atış arasında bir gecikmeye ihtiyacı vardır. Bu, bir oyuncunun en son ne zaman ateş ettiğini kontrol ederek kontrol edilebilir.
ToolController'ın en üstünde FIRE_RATE adında bir değişken tanımlayın. Bu, her atış arasındaki minimum süre olacaktır. Seçtiğiniz bir değeri verin; bu örnek 0.3 saniye kullanmaktadır.
Altında timeOfPreviousShot adında başka bir 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 = 1000local MAX_LASER_DISTANCE = 300local FIRE_RATE = 0.3local timeOfPreviousShot = 0Parametre almayan canShootWeapon adında bir fonksiyon oluşturun. Bu fonksiyon, önceki atıştan bu yana ne kadar zaman geçtiğini kontrol edecek ve true veya false döndürecektir.
local FIRE_RATE = 0.3local timeOfPreviousShot = 0-- Önceki atıştan bu yana yeterince zaman geçip geçmediğini kontrol etlocal function canShootWeapon()endlocal function getWorldMousePosition()Fonksiyonun içinde currentTime adında bir değişken tanımlayın; ona tick() fonksiyonunu çağırarak atayın. Bu, 1 Ocak 1970'ten (zamanı hesaplamak için yaygın olarak kullanılan bir tarih) bu yana geçen süreyi saniye cinsinden döndürür.
timeOfPreviousShot'ı currentTime'dan çıkarın ve sonuç FIRE_RATE'den küçükse false döndürün; aksi takdirde true döndürün.
-- Önceki atıştan bu yana yeterince zaman geçip geçmediğini kontrol etlocal function canShootWeapon()local currentTime = tick()if currentTime - timeOfPreviousShot < FIRE_RATE thenreturn falseendreturn trueendfireWeapon fonksiyonunun sonunda, silah her ateşlendiğinde timeOfPreviousShot'ı tick ile güncelleyin.
hitPosition = tool.Handle.Position + directionVectorendtimeOfPreviousShot = tick()LaserRenderer.createLaser(tool.Handle, hitPosition)endtoolActivated fonksiyonunun içinde, silahın ateşlenip ateşlenemeyeceğini kontrol etmek için bir if ifadesi oluşturun ve canShootWeapon'ı çağırın.
local function toolActivated()if canShootWeapon() thentool.Handle.Activate:Play()fireWeapon()endend
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 saniyelik gecikme olacağını bulmalısınız.
Oyuncuyu hasar verme
İstemciler, diğer istemcileri doğrudan hasar veremez; sunucu, bir oyuncu vurulduğunda hasar vermekten sorumlu olmalıdır.
İstemciler, bir karakterin vurulduğunu sunucuya bildirmek için bir RemoteEvent kullanabilir. Bunlar, hem istemci hem de sunucu tarafından görülebilir olan ReplicatedStorage içinde saklanmalıdır.
ReplicatedStorage içinde Events adında bir Klasör oluşturun.

Events klasörüne bir RemoteEvent ekleyin ve adını DamageCharacter olarak belirleyin.

ToolController içinde, scriptin 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.Parentlocal eventsFolder = ReplicatedStorage.Eventslocal MAX_MOUSE_DISTANCE = 1000local MAX_LASER_DISTANCE = 500fireWeapon içindeki "Oyuncu vuruldu" yazdırma ifadesini, characterModel değişkenini argüman olarak kullanarak DamageCharacter uzaktan olayını ateşleyecek bir Luau satırı ile değiştirin.
local characterModel = weaponRaycastResult.Instance:FindFirstAncestorOfClass("Model")if characterModel thenlocal humanoid = characterModel:FindFirstChildWhichIsA("Humanoid")if humanoid theneventsFolder.DamageCharacter:FireServer(characterModel, hitPosition)endendelse-- Maksimum lazer mesafesine göre son pozisyonu hesaplahitPosition = tool.Handle.Position + directionVectorend
Sunucu, olay ateşlendiğinde vurulan oyuncuya hasar vermelidir.
ServerScriptService içine bir Script ekleyin ve adını ServerLaserManager olarak belirleyin.

LASER_DAMAGE adında bir değişken tanımlayın ve değerini 10 olarak ayarlayın veya seçtiğiniz bir değeri verin.
playerFired ve characterToDamage adında iki parametreye sahip damageCharacter adında bir fonksiyon oluşturun.
Fonksiyonun içinde, karakterin Humanoid'ini bulun ve sağlığından LASER_DAMAGE'ı çıkarın.
damageCharacter fonksiyonunu Events klasöründeki DamageCharacter uzaktan olayına bağlayın.
local ReplicatedStorage = game:GetService("ReplicatedStorage")local eventsFolder = ReplicatedStorage.Eventslocal LASER_DAMAGE = 10function damageCharacter(playerFired, characterToDamage)local humanoid = characterToDamage:FindFirstChildWhichIsA("Humanoid")if humanoid then-- Karakterin sağlığını azalthumanoid.Health -= LASER_DAMAGEendend-- Olayları uygun fonksiyonlara bağlayıneventsFolder.DamageCharacter.OnServerEvent:Connect(damageCharacter)İki oyuncu ile blasterı test edin, yerel bir sunucu başlatarak. Diğer oyuncuya ateş ettiğinizde, sağlıkları LASER_DAMAGE'a atanan değere göre azalacaktır.
Diğer oyuncuların lazer ışınlarını render etme
Şu anda, lazer ışını, silahı ateşleyen istemci tarafından oluşturuluyor, bu yüzden sadece o istemci lazer ışınını görebiliyor.
Eğer lazer ışını sunucuda oluşturulursa, herkes bunu görebilir. Ancak, istemcinin silahı ateşlemesi ile sunucunun ateş hakkında bilgi alması arasında küçük bir gecikme olacaktır. Bu, silahı ateşleyen istemcinin, silahı etkinleştirdiklerinde lazer ışınını görmeleri ile lazer ışınını görmeleri arasında bir gecikme olacağı anlamına gelir; bu da silahın gecikmeli hissettirmesine neden olur.
Bu sorunu çözmek için, her istemci kendi lazer ışınlarını oluşturacaktır. Bu, silahı ateşleyen istemcinin lazer ışınını anında görmesini sağlar. Diğer istemciler, başka bir oyuncu ateş ettiğinde lazerin görünmesi arasında küçük bir gecikme yaşayacaklardır. Bu en iyi senaryodur: bir istemcinin lazerini diğer istemcilere iletmenin daha hızlı bir yolu yoktur.
Ateş eden istemci
Öncelikle, istemcinin sunucuya bir lazer ateşlediğini bildirmesi ve bitiş pozisyonunu sağlaması gerekir.
ReplicatedStorage'deki Events klasörüne bir RemoteEvent ekleyin ve adını LaserFired olarak belirleyin.

ToolController scriptindeki fireWeapon fonksiyonunu bulun. Fonksiyonun sonunda, LaserFired uzaktan olayını hitPosition'ı argüman olarak kullanarak ateşleyin.
hitPosition = tool.Handle.Position + directionVectorendtimeOfPreviousShot = tick()eventsFolder.LaserFired:FireServer(hitPosition)LaserRenderer.createLaser(tool.Handle, hitPosition)end
Sunucu
Sunucu şimdi, istemcinin ateş ettiğini bildiren olayı almalı ve tüm istemcilere lazer ışınının başlangıç ve bitiş pozisyonunu bildirmelidir, böylece onlar da lazer ışınını render edebilirler.
ServerLaserManager scriptinde, iki parametreye sahip playerFiredLaser adında bir fonksiyon oluşturun: playerFired ve endPosition.
Fonksiyonu LaserFired uzaktan olayına bağlayın.
-- Tüm istemcilere bir lazer ateşlendiğini bildirin, böylece lazeri görüntüleyebilirlerlocal function playerFiredLaser(playerFired, endPosition)end-- Olayları uygun fonksiyonlara bağlayıneventsFolder.DamageCharacter.OnServerEvent:Connect(damageCharacter)eventsFolder.LaserFired.OnServerEvent:Connect(playerFiredLaser)
Sunucunun lazerin başlangıç pozisyonunu alması gerekir. Bu istemciden gönderilebilir, ancak mümkün olduğunca istemciye güvenmemek en iyisidir. Karakterin silah tutma pozisyonu başlangıç pozisyonu olacaktır, bu yüzden sunucu bunu oradan bulabilir.
playerFiredLaser fonksiyonunun üstünde, player adında bir parametre ile getPlayerToolHandle adında bir fonksiyon oluşturun.
Aşağıdaki kodu kullanarak oyuncunun karakterinde silahı arayın ve tutma nesnesini döndürün.
local LASER_DAMAGE = 10-- Oyuncunun tuttuğu aracın tutma nesnesini bullocal function getPlayerToolHandle(player)local weapon = player.Character:FindFirstChildOfClass("Tool")if weapon thenreturn weapon:FindFirstChild("Handle")endend-- Tüm istemcilere bir lazer ateşlendiğini bildirin, böylece lazeri görüntüleyebilirlerlocal function playerFiredLaser(playerFired, endPosition)
Artık sunucu, LaserFired uzaktan olayında FireAllClients çağrısı yaparak, istemcilere render etmek için gereken bilgileri gönderebilir. Bu, lazeri ateşleyen oyuncu (böylece o oyuncunun istemcisi lazeri iki kez render etmez), lazerin başlangıç pozisyonu olan tutma ve lazerin bitiş pozisyonunu içerir.
playerFiredLaser fonksiyonunda, getPlayerToolHandle fonksiyonunu playerFired ile çağırın ve sonucu toolHandle adında bir değişkene atayın.
Eğer toolHandle varsa, playerFired, toolHandle ve endPosition argümanları ile LaserFired olayını tüm istemcilere ateşleyin.
-- Tüm istemcilere bir lazer ateşlendiğini bildirin, böylece lazeri görüntüleyebilirlerlocal function playerFiredLaser(playerFired, endPosition)local toolHandle = getPlayerToolHandle(playerFired)if toolHandle theneventsFolder.LaserFired:FireAllClients(playerFired, toolHandle, endPosition)endend
İstemcilerde render etme
Artık FireAllClients çağrıldığında, her istemci sunucudan lazer ışınını render etmek için bir olay alacaktır. Her istemci, sunucudan gönderilen silahın tutma pozisyonunu ve bitiş pozisyonu değerini kullanarak lazer ışınını render etmek için daha önceki LaserRenderer modülünü yeniden kullanabilir. Lazer ışınını ilk ateşleyen oyuncu, bu olayı göz ardı etmelidir, aksi takdirde iki lazer görecektir.
StarterPlayerScripts içinde ClientLaserManager adında bir LocalScript oluşturun.

Scriptin içinde LaserRenderer modülünü gereksin.
playerWhoShot, toolHandle ve endPosition parametrelerine sahip createPlayerLaser adında bir fonksiyon oluşturun.
Fonksiyonu Events klasöründeki LaserFired uzaktan olayına bağlayın.
Fonksiyonda, playerWhoShot'ın LocalPlayer ile eşit olmadığını kontrol etmek için bir if ifadesi kullanın.
If ifadesinin içinde, toolHandle ve endPosition argümanlarını kullanarak 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ülelocal function createPlayerLaser(playerWhoShot, toolHandle, endPosition)if playerWhoShot ~= Players.LocalPlayer thenLaserRenderer.createLaser(toolHandle, endPosition)endendeventsFolder.LaserFired.OnClientEvent:Connect(createPlayerLaser)İki oyuncu ile blasterı test edin, yerel bir sunucu başlatarak. Her iki istemciyi de monitörünüzün farklı taraflarına yerleştirin, böylece her iki pencereyi de görebilirsiniz. Bir istemcide ateş ettiğinizde, diğer istemcide lazer görünmelidir.
Ses efektleri
Ateş etme ses efekti şu anda sadece projeyi ateşleyen istemcide çalıyor. Diğer oyuncuların da bunu duyması için sesi çalma kodunu taşımak gerekecek.
ToolController scriptinde, toolActivated fonksiyonuna gidin ve Activate sesini çalan satırı kaldırın.
local function toolActivated()if canShootWeapon() thenfireWeapon()endendLaserRenderer içindeki createLaser fonksiyonunun sonunda, shootingSound adında bir değişken tanımlayın ve toolHandle'ın Activate sesini kontrol etmek için FindFirstChild() yöntemini kullanın.
shootingSound varsa, Play() fonksiyonunu çağırın.
laserPart.Parent = workspace-- Lazer ışınını Debris servisine ekleyin, böylece kaldırılır ve temizlenirDebris:AddItem(laserPart, SHOT_DURATION)-- Silahın ateş etme sesini çallocal shootingSound = toolHandle:FindFirstChild("Activate")if shootingSound thenshootingSound:Play()endend
Uzaktan olayları doğrulama ile güvence altına alma
Eğer sunucu, gelen isteklerden gelen verileri kontrol etmiyorsa, bir hacker uzaktan fonksiyonları ve olayları kötüye kullanabilir ve sunucuya sahte değerler gönderebilir. Bunu önlemek için sunucu tarafı doğrulaması kullanmak önemlidir.
Mevcut haliyle, DamageCharacter uzaktan olayı saldırıya çok açıktır. Hackerlar, bu olayı kullanarak istedikleri herhangi bir oyuncuya hasar verebilirler.
Doğrulama, sunucuya gönderilen değerlerin gerçekçi olup olmadığını kontrol etme sürecidir. Bu durumda, sunucu:
- Oyuncu ile lazerin vurduğu pozisyon arasındaki mesafenin belirli bir sınır içinde olup olmadığını kontrol etmelidir.
- Lazerin ateşlendiği silah ile vurulan pozisyon arasında raycast yaparak, atışın mümkün olup olmadığını ve herhangi bir duvardan geçip geçmediğini kontrol etmelidir.
İstemci
İstemci, sunucuya raycast ile vurulan pozisyonu göndermelidir, böylece mesafenin gerçekçi olup olmadığını kontrol edebilir.
ToolController'da, fireWeapon fonksiyonundaki DamageCharacter uzaktan olayının ateşlendiği satıra gidin.
hitPosition'ı bir argüman olarak ekleyin.
if characterModel thenlocal humanoid = characterModel:FindFirstChildWhichIsA("Humanoid")if humanoid theneventsFolder.DamageCharacter:FireServer(characterModel, hitPosition)endend
Sunucu
İstemci artık DamageCharacter uzaktan olayında ek bir parametre gönderiyor, bu yüzden ServerLaserManager'ı buna göre ayarlamak gerekecek.
ServerLaserManager scriptinde, damageCharacter fonksiyonuna hitPosition parametresini ekleyin.
function damageCharacter(playerFired, characterToDamage, hitPosition)local humanoid = characterToDamage:FindFirstChildWhichIsA("Humanoid")if humanoid then-- Karakterin sağlığını azalthumanoid.Health -= LASER_DAMAGEendendgetPlayerToolHandle fonksiyonunun altına, isHitValid adında üç parametreye sahip bir fonksiyon oluşturun: playerFired, characterToDamage ve hitPosition.
endlocal function isHitValid(playerFired, characterToDamage, hitPosition)end
İlk kontrol, vurulan pozisyon ile karakter arasındaki mesafeyi kontrol etmek olacaktır.
Scriptin en üstünde MAX_HIT_PROXIMITY adında bir değişken tanımlayın ve değerini 10 olarak atayın. Bu, vurulan pozisyon ile karakter arasındaki maksimum mesafe olacaktır. Bir tolerans gereklidir çünkü karakter, istemci olayı ateşlediğinden beri biraz hareket etmiş olabilir.
local ReplicatedStorage = game:GetService("ReplicatedStorage")local eventsFolder = ReplicatedStorage.Eventslocal LASER_DAMAGE = 10local MAX_HIT_PROXIMITY = 10isHitValid fonksiyonunda, karakter ile vurulan pozisyon arasındaki mesafeyi hesaplayın. Eğer mesafe MAX_HIT_PROXIMITY'den büyükse, false döndürün.
local function isHitValid(playerFired, characterToDamage, hitPosition)-- Vurulan pozisyon ile karakter arasındaki mesafeyi doğrulalocal characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitudeif characterHitProximity > MAX_HIT_PROXIMITY thenreturn falseendend
İkinci kontrol, ateş edilen silah ile vurulan pozisyon arasında bir raycast yapmayı içerecektir. Eğer raycast, karakter olmayan bir nesneyi döndürürse, atışın geçerli olmadığını varsayabilirsiniz çünkü bir şey atışı engellemiştir.
Aşağıdaki kodu bu kontrolü gerçekleştirmek için kopyalayın. Fonksiyonun sonunda true döndürün: eğer sona ulaşırsa, tüm kontroller geçmiştir.
local function isHitValid(playerFired, characterToDamage, hitPosition)-- Vurulan pozisyon ile karakter arasındaki mesafeyi doğrulalocal characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitudeif characterHitProximity > 10 thenreturn falseend-- Duvarlardan geçip geçmediğini kontrol etlocal 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)-- Eğer vurulan nesne karakter değilse, atışı göz ardı etif rayResult and not rayResult.Instance:IsDescendantOf(characterToDamage) thenreturn falseendendreturn trueenddamageCharacter fonksiyonunda validShot adında bir değişken tanımlayın. Bunu isHitValid fonksiyonunu playerFired, characterToDamage ve hitPosition argümanları ile çağırarak atayın.
Aşağıdaki if ifadesine, validShot'ın true olup olmadığını 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ı azalthumanoid.Health -= LASER_DAMAGEendend
Artık damageCharacter uzaktan olayı daha güvenli hale geldi ve çoğu oyuncunun bunu kötüye kullanmasını önleyecektir. Ancak bazı kötü niyetli oyuncular, doğrulamayı aşmanın yollarını bulabilir; uzaktan olayları güvenli tutmak sürekli bir çabadır.
Lazer blaster'ınız artık tamamlandı ve raycasting kullanarak temel bir vurma tespit sistemi ile donatıldı. Kullanıcı Girişini Tespit Etme eğitimine göz atarak lazer blaster'ınıza bir yeniden yükleme eylemi eklemeyi veya eğlenceli bir harita oluşturarak lazer blaster'ınızı diğer oyuncularla denemeyi 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 bu yana 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 başlangıç noktasından yönüne doğru raycast yap
local raycastResult = workspace:Raycast(screenToWorldRay.Origin, directionVector)
if raycastResult then
-- Kesişme 3D noktasını döndür
return raycastResult.Position
else
-- Hiçbir nesne vurulmadı, bu yüzden rayin 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ı ateşlemek için yön, maksimum mesafe ile çarpılmış
local directionVector = targetDirection * MAX_LASER_DISTANCE
-- Oyuncunun karakterini göz ardı edin, böylece kendilerine zarar vermelerini önleyin
local weaponRaycastParams = RaycastParams.new()
weaponRaycastParams.FilterDescendantsInstances = {Players.LocalPlayer.Character}
local weaponRaycastResult = workspace:Raycast(tool.Handle.Position, directionVector, weaponRaycastParams)
-- Başlangıç ve bitiş pozisyonu arasında herhangi bir nesne vurulup vurulmadığını kontrol et
local hitPosition
if weaponRaycastResult then
hitPosition = weaponRaycastResult.Position
-- Vurulan nesne bir karakter modelinin çocuğu olacaktır
-- Eğer modelde bir humanoid bulunursa, muhtemelen 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ıç pozisyonundan bir bitiş pozisyonuna 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ı Debris servisine ekleyin, böylece kaldırılır ve temizlenir
Debris:AddItem(laserPart, SHOT_DURATION)
-- Silahın ateş etme sesini çal
local shootingSound = toolHandle:FindFirstChild("Activate")
if shootingSound then
shootingSound:Play()
end
end
return LaserRendererServerLaserManager
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local eventsFolder = ReplicatedStorage.Events
local LASER_DAMAGE = 10
local MAX_HIT_PROXIMITY = 10
-- Oyuncunun tuttuğu aracın tutma nesnesini bul
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 pozisyon ile karakter 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 bir lazer ateşlendiğini bildirin, 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)