본문 바로가기

로블록스 개발 중급

Blaster 제작 : 레이저빔 렌더링(2/3)

레이저빔의 명중까지 완성했으니 이젠 레이저빔의 렌더링에 대해 살펴보자.

레이저의 포지션 정하기

블래스터는 뻘건 광선으로 렌더링하기로 한다. 나중에 다른 스크립트에서 재사용할 수 있게 모듈스크립트(ModuleScript)로 작성한다. StarterPlayer 아래 StarterPlayerScrips 아래에 LaserRenderer라는 이름의 모듈스크립트를 추가한다.

모듈 테이블의 이름도 LaserRenderer로 바꾼다. 그리고 필요한 변수/상수와 함수를 추가한다.

local LaserRenderer = {}
 
-- 레이져가 보여지는 시간
local SHOT_DURATION = 0.15 
 
-- 모듈의 함수는 모듈명.함수명으로 정의한다.
-- 시작 위치에서 끝 위치까지 레이져를 렌더링하는 함수 createLaser
function LaserRenderer.createLaser(toolHandle, endPosition)
 
end
 
return LaserRenderer
  • 시작점(startPosition)은 블래스터의 위치이다.
  • 시작점과 끝점의 거리(laserDistance)를 (endPosition - startPosition)을 하고 Magnitude 속성으로 계산한다.
function LaserRenderer.createLaser(toolHandle, endPosition)
    -- 시작점은 블래스터(툴)의 핸들 위치
    local startPosition = toolHandle.Position
    - 시작점과 끝점의 거리 구하기
    local laserDistance = (startPosition - endPosition).Magnitude
end

레이저 빔의 렌더링을 위한 마지막 지점을 laserCFrame이라고 하자. 

이 위치는 레이저빔의 시작과 끝의 중간지점(laserDistance/2)이다. CFrame.lookAt를 사용하여  레이저빔의 시작과 끝을 향하는 새 CFrame을 만들고, 중간점을 얻으려면  laserDistance의 절반인 Z축 값의 마이너스를 곱한다.

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

레이저 파트 만들기

이제 레이저 빔을 생성할 위치를 알았으므로 이제 레이저 빔을 렌더링하자. 레이저 빔 모양을 내기 위해서 파트에게 Neon 메터리얼 속성을 주는 것만으로도 어느정도 비슷하게 보일 수 있다.

1. 소스 코드에서 파트를 생성하고 laserPart로 이름을 바꾸고 속성을 수정하자.

  • Size 속성: Vector3.new(0.2, 0.2, laserDistance)
  • CFrame 속성: 위에서 계산한 laserCFrame
  • Anchored 속성: true
  • CanCollide 속성: false
  • Color 속성: Color3.fromRGB(225, 0, 0) (벌건 색)
  • Material 속성: Enum.Material.Neon (네온 메터리얼은 스스로 은은하게 빛이 나는 속성이 있다.)

2. 레이저 빔의 상위노드는 workspace가 될 것이다.

3. Debris 서비스에 추가하므로써 일정 시간(SHOT_DURATION) 후에 알아서 제거되도록 한다.

function LaserRenderer.createLaser(toolHandle, endPosition)
    local startPosition = toolHandle.Position
 
    local laserDistance = (startPosition - endPosition).Magnitude
    local laserCFrame = CFrame.lookAt(startPosition, endPosition) * CFrame.new(0, 0, -laserDistance / 2)
 
    local laserPart = Instance.new("Part")
    laserPart.Size = Vector3.new(0.2, 0.2, laserDistance)
    laserPart.CFrame = laserCFrame
    laserPart.Anchored = true
    laserPart.CanCollide = false
    laserPart.Color = Color3.fromRGB(225, 0, 0)
    laserPart.Material = Enum.Material.Neon
    laserPart.Parent = workspace
 
    -- Debris 서비스에게 일정시간후 제거하도록 함
    game.Debris:AddItem(laserPart, SHOT_DURATION)
end

이제 레이저빔 자체의 렌더링 모듈 스크립트는 완료되었으므로 ToolController에서 호출하면 된다. ToolController 스크립트 상단에 LaserRenderer라는 변수를 선언하고 PalyerScripts에 있는 LaserRenderer 모듈 스크립트를 require()한다.

local UserInputService = game:GetService("UserInputService")
local Players = game:GetService("Players")

-- 모듈 스크립트를 require() 해서 LaserRenderer로 사용이 가능하다
local LaserRenderer = require(Players.LocalPlayer.PlayerScripts.LaserRenderer)
 
local tool = script.Parent

fireWeapon 함수의 아래 부분에서 Tool의 Handle과 hitPosition을 인수로 사용하여 LaserRenderer 모듈의 createLaser() 함수를 호출한다.

        hitPosition = tool.Handle.Position + directionVector
    end
 
    LaserRenderer.createLaser(tool.Handle, hitPosition)
end

테스트를 해보자. 마우스 클릭에 의해 Tool이 활성화될 때마다 무기와 마우스 사이에 레이저 빔이 보여야 한다.

 

레이저 빔 발사 빈도 조절

테스트를 해보면 알겠지만, 지금의 레이저 빔은 마우스를 클릭할 때마다 발사되어서 게임에서 사용하기엔 무리가 있다. 레이저 빔의 발사에 시간 간격을 주고 보다 현실성이 있게 고치자. 한번 발사를 하고 나서 시간을 체크해서 일정 시간이 지났는지를 보고나서 그 후에 다시 발사를 할 수 있도록 한다.

 FIRE_RATE 상수는 각 레이저빔간의 시간간격이다. 여기서는 0.3초로 한다. timeOfPreviousShot 변수는 레이저빔을 발사할 때의 시간을 저장해두는 용도로 쓴다. 이 값은 발사할 때마다 갱신된다.

local MAX_MOUSE_DISTANCE = 1000
local MAX_LASER_DISTANCE = 300
local FIRE_RATE = 0.3
local timeOfPreviousShot = 0

canShootWeapon() 함수를 정의한다. 이 함수는 마지막 발사후에 FIRE_RATE만큼 시간이 지났는지 아닌지를 판단해 준다. tick()함수를 통해 현재 시간을 얻어오고, 그 현재시간과 timeOfPreviousShot 과의 차이가 FIRE_RATE보다 크면 true, 작으면 false이다.

local FIRE_RATE = 0.3
local timeOfPreviousShot = 0
 
-- 레이저빔 발사후에 충분한 시간이 흘렀는지를 체크
local function canShootWeapon()
    local currentTime = tick()
    if currentTime - timeOfPreviousShot < FIRE_RATE then
        return false
    end
    return true
end
 
local function getWorldMousePosition()

fireWeapon() 함수 마지막 부분에 발사할 때마다 timeOfPreviousShot 를 tick() -현재시간으로 업데이트 해준다.

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

toolActivated() 함수 안에서 canShootWeapon()함수로 체크하여 true를 반환할 때만 실제로 발사가 되도록 한다.

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

테스트를 하면서 마우스클릭을 아무리 빨리하더라도 0.3초정도의 시간의 지연이 있음을 확인하자.