You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Lua脚本报错:new函数参数错误(期望Vector3,得到Vector2)求解

Skriblio类游戏Lua错误修复:Vector3/Vector2类型不匹配

错误详情

运行脚本时触发错误:Lua error : invalid argument #1 to 'new' (Vector3 expected, got Vector2),错误位于第12行。

原代码

local UserInputService = game:GetService("UserInputService")
local currentTool = "Line" -- Ganti dengan alat default Anda
local currentColor = Color3.new(0, 0, 0) -- Ganti dengan warna default Anda
local canvas = script.Parent -- Ganti dengan objek "canvas" yang sesuai

local function drawLine(startPoint, endPoint)
    local line = Instance.new("Part")
    line.Anchored = true
    line.Material = Enum.Material.SmoothPlastic
    line.BrickColor = BrickColor.new(currentColor)
    line.Size = Vector3.new((startPoint - endPoint).Magnitude, 0.2, 0.2)
    line.CFrame = CFrame.new((startPoint + endPoint) / 2, Vector3.new(endPoint.X, endPoint.Y, 0))
    line.Parent = canvas
end

local function drawCircle(center, radius)
    local circle = Instance.new("Part")
    circle.Anchored = true
    circle.Material = Enum.Material.SmoothPlastic
    circle.BrickColor = BrickColor.new(currentColor)
    circle.Shape = Enum.PartType.Cylinder
    circle.Size = Vector3.new(radius * 2, 0.2, radius * 2)
    circle.CFrame = CFrame.new(center) + Vector3.new(0, 0.1, 0)
    circle.Parent = canvas
end

local function drawFreehand(startPoint, endPoint)
    local line = Instance.new("Part")
    line.Anchored = true
    line.Material = Enum.Material.SmoothPlastic
    line.BrickColor = BrickColor.new(currentColor)
    line.Size = Vector3.new(0.2, 0.2, (startPoint - endPoint).Magnitude)
    line.CFrame = CFrame.new((startPoint + endPoint) / 2, Vector3.new(endPoint.X, endPoint.Y, 0))
    line.Parent = canvas
end

local function erase(startPoint, endPoint)
    -- Dapatkan semua objek anak di dalam "canvas"
    local canvasChildren = canvas:GetChildren()

    -- Iterasi melalui semua objek anak dan periksa apakah mereka berada dalam wilayah yang ditentukan
    for _, child in ipairs(canvasChildren) do
        if child:IsA("BasePart") then
            local position = child.Position
            if position.X >= startPoint.X and position.X <= endPoint.X and
                position.Y >= startPoint.Y and position.Y <= endPoint.Y then
                child:Destroy()
            end
        end
    end
end
UserInputService.InputBegan:Connect(function(input)
    if input.UserInputType == Enum.UserInputType.MouseButton1 then
        local startPoint = UserInputService:GetMouseLocation()

        if currentTool == "Line" then
            -- Menggambar garis
            UserInputService.InputEnded:Wait()
            local endPoint = UserInputService:GetMouseLocation()
            drawLine(startPoint, endPoint)
        elseif currentTool == "Circle" then
            -- Menggambar lingkaran
            UserInputService.InputEnded:Wait()
            local endPoint = UserInputService:GetMouseLocation()
            local radius = (startPoint - endPoint).Magnitude
            drawCircle(startPoint, radius)
        elseif currentTool == "Pen" then
            -- Menggambar secara bebas
            local prevPoint = startPoint
            repeat
                local currentPoint = UserInputService:GetMouseLocation()
                drawFreehand(prevPoint, currentPoint)
                prevPoint = currentPoint
                wait()
            until not UserInputService:IsMouseButtonPressed(Enum.UserInputType.MouseButton1)
        elseif currentTool == "Eraser" then
            -- Menghapus
            UserInputService.InputEnded:Wait()
            local endPoint = UserInputService:GetMouseLocation()
            erase(startPoint, endPoint)
        end
    end
end)

问题根源

  1. UserInputService:GetMouseLocation()返回的是Vector2类型(仅包含屏幕X/Y坐标),但Roblox中Part的CFrame、Position等属性需要Vector3类型(3D空间坐标,含X/Y/Z)。
  2. 第12行的CFrame.new((startPoint + endPoint)/2, ...)中,(startPoint + endPoint)/2是Vector2,不符合CFrame构造函数的参数要求,直接触发类型错误。
  3. 其他函数(如drawCircle、erase)也存在同样的Vector2/Vector3不兼容问题。

修改方案

步骤1:添加屏幕坐标转3D世界坐标的函数

首先获取当前相机,并实现转换逻辑(假设Canvas位于Z=0的平面,可根据实际场景调整Z值):

local Camera = game.Workspace.CurrentCamera

-- 将屏幕坐标转换为Canvas所在平面的3D坐标
local function screenToWorld(screenPos)
    -- 从相机发射射线到屏幕位置
    local ray = Camera:ViewportPointToRay(screenPos.X, screenPos.Y)
    -- 计算射线与Z=0平面的交点(Canvas所在平面)
    local distance = -ray.Origin.Z / ray.Direction.Z
    return ray.Origin + ray.Direction * distance
end

步骤2:替换所有鼠标位置的获取逻辑

将所有UserInputService:GetMouseLocation()替换为screenToWorld(UserInputService:GetMouseLocation()),确保传入函数的是Vector3类型的坐标。

步骤3:修复函数中的Vector3使用问题

  • drawLine和drawFreehand中,CFrame.new的第二个参数直接使用转换后的endPoint(已经是Vector3),无需手动构造Vector3。
  • drawCircle中CFrame.new(center)的center现在是Vector3,无需额外处理。

修改后的完整代码

local UserInputService = game:GetService("UserInputService")
local Camera = game.Workspace.CurrentCamera
local currentTool = "Line" -- Ganti dengan alat default Anda
local currentColor = Color3.new(0, 0, 0) -- Ganti dengan warna default Anda
local canvas = script.Parent -- Ganti dengan objek "canvas" yang sesuai

-- 将屏幕坐标转换为Canvas所在平面的3D坐标(假设Canvas在Z=0平面)
local function screenToWorld(screenPos)
    local ray = Camera:ViewportPointToRay(screenPos.X, screenPos.Y)
    local distance = -ray.Origin.Z / ray.Direction.Z
    return ray.Origin + ray.Direction * distance
end

local function drawLine(startPoint, endPoint)
    local line = Instance.new("Part")
    line.Anchored = true
    line.Material = Enum.Material.SmoothPlastic
    line.BrickColor = BrickColor.new(currentColor)
    line.Size = Vector3.new((startPoint - endPoint).Magnitude, 0.2, 0.2)
    -- 直接使用Vector3类型的坐标构造CFrame
    line.CFrame = CFrame.new((startPoint + endPoint) / 2, endPoint)
    line.Parent = canvas
end

local function drawCircle(center, radius)
    local circle = Instance.new("Part")
    circle.Anchored = true
    circle.Material = Enum.Material.SmoothPlastic
    circle.BrickColor = BrickColor.new(currentColor)
    circle.Shape = Enum.PartType.Cylinder
    circle.Size = Vector3.new(radius * 2, 0.2, radius * 2)
    -- center已经是Vector3,直接使用
    circle.CFrame = CFrame.new(center) + Vector3.new(0, 0.1, 0)
    circle.Parent = canvas
end

local function drawFreehand(startPoint, endPoint)
    local line = Instance.new("Part")
    line.Anchored = true
    line.Material = Enum.Material.SmoothPlastic
    line.BrickColor = BrickColor.new(currentColor)
    line.Size = Vector3.new(0.2, 0.2, (startPoint - endPoint).Magnitude)
    -- 直接使用Vector3类型的坐标构造CFrame
    line.CFrame = CFrame.new((startPoint + endPoint) / 2, endPoint)
    line.Parent = canvas
end

local function erase(startPoint, endPoint)
    local canvasChildren = canvas:GetChildren()
    for _, child in ipairs(canvasChildren) do
        if child:IsA("BasePart") then
            local position = child.Position
            -- 现在startPoint和endPoint都是Vector3,直接比较X/Y即可
            if position.X >= math.min(startPoint.X, endPoint.X) and position.X <= math.max(startPoint.X, endPoint.X) and
                position.Y >= math.min(startPoint.Y, endPoint.Y) and position.Y <= math.max(startPoint.Y, endPoint.Y) then
                child:Destroy()
            end
        end
    end
end

UserInputService.InputBegan:Connect(function(input)
    if input.UserInputType == Enum.UserInputType.MouseButton1 then
        -- 转换鼠标位置为3D坐标
        local startPoint = screenToWorld(UserInputService:GetMouseLocation())

        if currentTool == "Line" then
            UserInputService.InputEnded:Wait()
            local endPoint = screenToWorld(UserInputService:GetMouseLocation())
            drawLine(startPoint, endPoint)
        elseif currentTool == "Circle" then
            UserInputService.InputEnded:Wait()
            local endPoint = screenToWorld(UserInputService:GetMouseLocation())
            local radius = (startPoint - endPoint).Magnitude
            drawCircle(startPoint, radius)
        elseif currentTool == "Pen" then
            local prevPoint = startPoint
            repeat
                local currentPoint = screenToWorld(UserInputService:GetMouseLocation())
                drawFreehand(prevPoint, currentPoint)
                prevPoint = currentPoint
                task.wait()
            until not UserInputService:IsMouseButtonPressed(Enum.UserInputType.MouseButton1)
        elseif currentTool == "Eraser" then
            UserInputService.InputEnded:Wait()
            local endPoint = screenToWorld(UserInputService:GetMouseLocation())
            erase(startPoint, endPoint)
        end
    end
end)

额外优化说明

  • 将wait()替换为task.wait(),这是Roblox推荐的现代等待方式。
  • 在erase函数中增加math.min/math.max处理,确保不管鼠标拖拽方向如何,区域判断都正确。

内容的提问来源于stack exchange,提问作者Adrian Nugroho

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 17:42:01