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.

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

Betiğin 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 ekrandaki 2D fare konumunu alın. Bunu mouseLocation adlı 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 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.
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ş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ı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.
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ş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 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ş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 orijinal yönüne doğru ışın tarama yaplocal 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ğ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 arazinin yüzeyinde doğrudan bir nokta. |
| Material | Kesişme 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 üzerine geldiği nesnenin konumu olacaktır. Eğer fare, MAX_MOUSE_DISTANCE mesafesindeki hiçbir nesneye çarpmıyorsa, raycastResult nil olur.
raycastResult'ın var olup olmadığını kontrol etmek için bir if durumu oluşturun.
Eğer raycastResult değere sahipse, onun Position özelliğini döndürün.
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.
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.Parentlocal MAX_MOUSE_DISTANCE = 1000local MAX_LASER_DISTANCE = 500getWorldMousePosition fonksiyonunun altında fireWeapon adında bir fonksiyon oluşturun.
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 hesaplareturn screenToWorldRay.Origin + directionVectorendendlocal function fireWeapon()local mouseLocation = getWorldMousePosition()endlocal 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.
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.
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ınlocal targetDirection = (mouseLocation - tool.Handle.Position).UnitenddirectionVector adında bir değişken tanımlayıp bunu targetDirection 'ı MAX_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_DISTANCEend
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.
fireWeapon fonksiyonuna devam edin ve weaponRaycastParams adlı bir değişken tanımlayın. Ona yeni bir RaycastParams nesnesi atayın.
Oyuncunun yerel karakter'ine ait bir tablo oluşturun ve bunu weaponRaycastParams.FilterDescendantsInstances özelliğine atayın.
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.
Boş bir hitPosition adlı değişken tanımlayın.
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 etlocal hitPositionif weaponRaycastResult thenhitPosition = weaponRaycastResult.PositionendEğ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 etlocal hitPositionif weaponRaycastResult thenhitPosition = weaponRaycastResult.Positionelse-- Maksimum lazer mesafesine göre son pozisyonu hesaplahitPosition = tool.Handle.Position + directionVectorendendtoolActivated 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.
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 etlocal hitPositionif weaponRaycastResult thenhitPosition = 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 karakteridirlocal characterModel = weaponRaycastResult.Instance:FindFirstAncestorOfClass("Model")if characterModel thenlocal humanoid = characterModel:FindFirstChildWhichIsA("Humanoid")if humanoid thenprint("Oyuncuya vuruldu")endendelse-- Maksimum lazer mesafesine göre son pozisyonu hesaplahitPosition = tool.Handle.Position + directionVectorend
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.
LaserRenderer adında bir ModuleScript oluşturun ve bunu StarterPlayer altında StarterPlayerScripts'e ebeveyn yapın.

Betiği açın ve modül tablosunun adını betiğin adı olan LaserRenderer olarak değiştirin.
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.
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şturfunction LaserRenderer.createLaser(toolHandle, endPosition)endreturn LaserRendererstartPosition 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.
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.Positionlocal laserDistance = (startPosition - endPosition).MagnitudeendlaserCFrame 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.Positionlocal laserDistance = (startPosition - endPosition).Magnitudelocal 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.
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:
- Size: Vector3.new(0.2, 0.2, laserDistance)
- CFrame: laserCFrame
- Anchored: true
- CanCollide: false
- Color: Color3.fromRGB(225, 0, 0) (açık kırmızı renk)
- Material: Enum.Material.Neon
laserPart'ı Workspace'e ebeveyn yapın.
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.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ı süre sonunda kaldırılması için Debris hizmetine ekleyinDebris:AddItem(laserPart, SHOT_DURATION)end
Artık lazer ışınını render etmek için fonksiyon tamamlandı, bu ToolController tarafından çağrılabilir.
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.ParentfireWeapon fonksiyonunun sonunda, LaserRenderer'ın createLaser fonksiyonunu çağırarak alet tutma ve hitPosition'u argüman olarak verin.
-- Maksimum lazer mesafesine göre son pozisyonu hesaplahitPosition = tool.Handle.Position + directionVectorendLaserRenderer.createLaser(tool.Handle, hitPosition)endOyunu 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.
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.
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 = 1000local MAX_LASER_DISTANCE = 300local FIRE_RATE = 0.3local timeOfPreviousShot = 0Parametre 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.3local timeOfPreviousShot = 0-- Önceki atıştan 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() 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.
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 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 durumu 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 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.
ReplicatedStorage içinde Events adında bir Klasör oluşturun.

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

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.Parentlocal eventsFolder = ReplicatedStorage.Eventslocal MAX_MOUSE_DISTANCE = 1000local MAX_LASER_DISTANCE = 500fireWeapon 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 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
Şimdi sunucu, uzaktan çağırma olayı tetiklendiğinde, vurulan oyuncuya hasar vermelidir.
ServerScriptService içine bir Script ekleyin ve adını ServerLaserManager olarak koyun.

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.
playerFired ve characterToDamage adında iki parametreye sahip damageCharacter adlı bir fonksiyon oluşturun.
Fonksiyon içinde karakterin Humanoid'ini bulun ve sağlığından LASER_DAMAGE'ı çıkarın.
DamageCharacter uzaktan olayına damageCharacter fonksiyonunu 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 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.
ReplicatedStorage'deki Events klasörüne bir RemoteEvent ekleyin ve adını LaserFired koyun.

ToolController betiğindeki fireWeapon fonksiyonunu bulun. Fonksiyonun sonunda LaserFired uzaktan olayını göndererek hitPosition'ı argüman olarak kullanın.
hitPosition = tool.Handle.Position + directionVectorendtimeOfPreviousShot = 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.
ServerLaserManager betiğinde, iki parametreye sahip playerFiredLaser adında bir fonksiyon oluşturun؛ playerFired ve endPosition adında.
Fonksiyonu LaserFired uzaktan olayına bağlayın.
-- Tüm istemcilere lazerin ateşlendiğini bildir, 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 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.
playerFiredLaser fonksiyonunun üstüne, player parametresi ile birlikte getPlayerToolHandle adını veren bir fonksiyon oluşturun.
İş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 bulunlocal function getPlayerToolHandle(player)local weapon = player.Character:FindFirstChildOfClass("Tool")if weapon thenreturn weapon:FindFirstChild("Handle")endend-- Tüm istemcilere lazerin ateşlendiğini bildir, böylece lazeri görüntüleyebilirlerlocal 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.
playerFiredLaser fonksiyonunda, getPlayerToolHandle fonksiyonunu playerFired ile çağırın ve değerini toolHandle adında bir değişkene atayın.
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üleyebilirlerlocal function playerFiredLaser(playerFired, endPosition)local toolHandle = getPlayerToolHandle(playerFired)if toolHandle theneventsFolder.LaserFired:FireAllClients(playerFired, toolHandle, endPosition)endend
İ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.
StarterPlayerScripts içinde ClientLaserManager adında bir LocalScript oluşturun.

Betik içinde LaserRenderer modülünü çağırın.
playerWhoShot, toolHandle ve endPosition parametreleri ile createPlayerLaser adında bir fonksiyon oluşturun.
Bu fonksiyonu Events klasöründeki LaserFired uzaktan olayı ile bağlayın.
Fonksiyonda bir if durumu kullanarak playerWhoShot'ın LocalPlayer'a eşit olmayan durumunu kontrol edin.
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ülelocal function createPlayerLaser(playerWhoShot, toolHandle, endPosition)if playerWhoShot ~= Players.LocalPlayer thenLaserRenderer.createLaser(toolHandle, endPosition)endendeventsFolder.LaserFired.OnClientEvent:Connect(createPlayerLaser)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.
ToolController betiğinde toolActivated fonksiyonuna gidin ve etkinleştir sesini oynatan satırı kaldırın.
local function toolActivated()if canShootWeapon() thenfireWeapon()endendLaserRenderer'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.
Eğer shootingSound var ise, Play() fonksiyonunu çağırın.
laserPart.Parent = workspace-- Lazer ışınının Debris hizmetine eklenmesini sağlamak içinDebris:AddItem(laserPart, SHOT_DURATION)-- Silahın ateşleme sesini çallocal shootingSound = toolHandle:FindFirstChild("Activate")if shootingSound thenshootingSound:Play()endend
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.
ToolController içinde, fireWeapon fonksiyonundaki DamageCharacter uzaktan olayının ateşlendiği satıra gidin.
hitPosition'ı 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, bir ek parametre gönderdiğinden, ServerLaserManager'ın bunu kabul edecek şekilde ayarlanması gerekir.
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ı azalthumanoid.Health -= LASER_DAMAGEendendgetPlayerToolHandle fonksiyonunun altına, üç parametre alan isHitValid adında bir fonksiyon oluşturun; playerFired, characterToDamage ve hitPosition.
endlocal function isHitValid(playerFired, characterToDamage, hitPosition)end
İlk kontrol, vurulan pozisyon ile vurulan karakter arasındaki mesafenin kontrolüdür.
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.Eventslocal LASER_DAMAGE = 10local MAX_HIT_PROXIMITY = 10isHitValid 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ğrulalocal characterHitProximity = (characterToDamage.HumanoidRootPart.Position - hitPosition).Magnitudeif characterHitProximity > MAX_HIT_PROXIMITY thenreturn falseendend
İ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.
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ğ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. Değerini, isHitValid fonksiyonunu playerFired, characterToDamage ve hitPosition argümanlarıyla çağırdığınızda alın.
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ı azalthumanoid.Health -= LASER_DAMAGEendend
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)