223 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
			
		
		
	
	
			223 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
local DataTableUtils = require("Utils.DataTableUtils")
 | 
						||
local ESlateVisibility = import("ESlateVisibility")
 | 
						||
local SlateBlueprintLibrary = import("SlateBlueprintLibrary")
 | 
						||
local WidgetLayoutLibrary = import("WidgetLayoutLibrary")
 | 
						||
local BusyGamePlayLibrary = import("BusyGamePlayLibrary")
 | 
						||
 | 
						||
local CUT_MASK_DISPLAY_TIME = 1.2  -- 刀光显示的时长
 | 
						||
local CUT_MASK_FADEOUT_TIME = 0.6  -- 刀光开始渐隐的时间点
 | 
						||
 | 
						||
--- @class PreCookCenterWidget
 | 
						||
--- @field ImgContainer table
 | 
						||
--- @field ImgCookMaterial table
 | 
						||
--- @field BtnMain table
 | 
						||
local PreCookCenterWidget = {}
 | 
						||
 | 
						||
function PreCookCenterWidget:ctor()
 | 
						||
    self.mouse_tracks = {}      -- 记录当前鼠标的轨迹
 | 
						||
    self.rendering_tracks = {}  -- 正在被渲染的刀光
 | 
						||
    self.is_pressed = false
 | 
						||
end
 | 
						||
 | 
						||
 | 
						||
function PreCookCenterWidget:OnInitialized()
 | 
						||
    self.bHasScriptImplementedTick = true
 | 
						||
 | 
						||
    self:BP_BindLuaEnhancedInput(self.IA_TouchBegin, function()
 | 
						||
        self.is_pressed = true
 | 
						||
        self.mouse_tracks = {}
 | 
						||
        self.rendering_tracks = {self.mouse_tracks}
 | 
						||
        print("new track start")
 | 
						||
    end)
 | 
						||
 | 
						||
    self:BP_BindLuaEnhancedInput(self.IA_TouchEnd, function()
 | 
						||
        self.is_pressed = false
 | 
						||
        self.mouse_tracks = {}
 | 
						||
        print("track end")
 | 
						||
    end)
 | 
						||
 | 
						||
    self.BtnMain:SetVisibility(ESlateVisibility.Collapsed)
 | 
						||
end
 | 
						||
 | 
						||
function PreCookCenterWidget:Construct()
 | 
						||
    -- self.bHasScriptImplementedTick = true
 | 
						||
end
 | 
						||
 | 
						||
function PreCookCenterWidget:Destruct()
 | 
						||
 | 
						||
end
 | 
						||
 | 
						||
function PreCookCenterWidget:SetEmpty()
 | 
						||
    self.ImgContainer:SetVisibility(ESlateVisibility.Collapsed)
 | 
						||
    self.ImgCookMaterial:SetVisibility(ESlateVisibility.Collapsed)
 | 
						||
end
 | 
						||
 | 
						||
function PreCookCenterWidget:AddContainer(pre_cook_contianer_id)
 | 
						||
    local row = DataTableUtils.GetDataTableRow("PreCookItemConfig", pre_cook_contianer_id)
 | 
						||
    if not row then return end
 | 
						||
    self.ImgContainer:SetBrushFromSoftTexture(row.CenterDisplayResource, true)
 | 
						||
    self.ImgContainer:SetVisibility(ESlateVisibility.SelfHitTestInvisible)
 | 
						||
end
 | 
						||
 | 
						||
function PreCookCenterWidget:RemoveContainer()
 | 
						||
    self.ImgContainer:SetVisibility(ESlateVisibility.Collapsed)
 | 
						||
end
 | 
						||
 | 
						||
function PreCookCenterWidget:AddCookMaterial(pre_cook_material_id)
 | 
						||
    local row = DataTableUtils.GetDataTableRow("PreCookItemConfig", pre_cook_material_id)
 | 
						||
    if not row then return end
 | 
						||
    self.ImgCookMaterial:SetBrushFromSoftTexture(row.CenterDisplayResource, true)
 | 
						||
    self.ImgCookMaterial:SetVisibility(ESlateVisibility.SelfHitTestInvisible)
 | 
						||
end
 | 
						||
 | 
						||
--- 从起点到终点画一条线,以这条线为新的x坐标轴,将所有的点坐标映射到新坐标系下
 | 
						||
local function TransformCurveToEndpointAxes(points)
 | 
						||
    local A = points[1]
 | 
						||
    local B = points[#points]
 | 
						||
 | 
						||
    -- 计算向量AB
 | 
						||
    local dx = B.X - A.X
 | 
						||
    local dy = B.Y - A.Y
 | 
						||
    local len = math.sqrt(dx * dx + dy * dy)
 | 
						||
    if len == 0 then
 | 
						||
        return {}
 | 
						||
        -- error("Start and end points are the same, cannot define X-axis.")
 | 
						||
    end
 | 
						||
 | 
						||
    -- 计算X轴单位向量
 | 
						||
    local ux = dx / len
 | 
						||
    local uy = dy / len
 | 
						||
 | 
						||
    -- 计算Y轴单位向量(逆时针旋转90度)
 | 
						||
    local vx = -uy
 | 
						||
    local vy = ux
 | 
						||
 | 
						||
    -- 映射所有点到新坐标系
 | 
						||
    local new_points = {}
 | 
						||
    for i, point in ipairs(points) do
 | 
						||
        local apx = point.X - A.X
 | 
						||
        local apy = point.Y - A.Y
 | 
						||
        local new_x = apx * ux + apy * uy  -- 点积与X轴单位向量
 | 
						||
        local new_y = apx * vx + apy * vy  -- 点积与Y轴单位向量
 | 
						||
        new_points[i] = {X = new_x, Y = new_y}
 | 
						||
    end
 | 
						||
 | 
						||
    return new_points
 | 
						||
end
 | 
						||
 | 
						||
 | 
						||
--- 将曲线的Y坐标规范到(-0.5,0.5)的范围内,供材质使用
 | 
						||
local function NormalizeCurveYToHalfRange(points)
 | 
						||
    if #points < 2 then return end
 | 
						||
    local length = points[#points].X - points[1].X
 | 
						||
    for _, point in pairs(points) do
 | 
						||
        point.Y = - point.Y / length  -- 临时加个取反
 | 
						||
        point.X = point.X / length
 | 
						||
    end
 | 
						||
    return points
 | 
						||
end
 | 
						||
 | 
						||
 | 
						||
local function UpdateCutMaskData(rendering_tracks, delta_time)
 | 
						||
    local new_visible_tracks = {}
 | 
						||
    for _, track in ipairs(rendering_tracks) do
 | 
						||
        local is_visible = false
 | 
						||
        for _, point in pairs(track) do
 | 
						||
            local remain = math.max(point.remain - delta_time, 0)
 | 
						||
            point.remain = remain
 | 
						||
            if remain > 0 then is_visible = true end
 | 
						||
        end
 | 
						||
        if is_visible then
 | 
						||
            table.insert(new_visible_tracks, track)
 | 
						||
        end
 | 
						||
    end
 | 
						||
    return new_visible_tracks
 | 
						||
end
 | 
						||
 | 
						||
local function DrawCutMaskImage(widget, mouse_tracks)
 | 
						||
    local FVector2D = import("Vector2D")
 | 
						||
    local FWidgetTransform = import("WidgetTransform")
 | 
						||
    -- 设置图片合理的位移、旋转、缩放的参数
 | 
						||
    local translation, scale = FVector2D(), FVector2D()
 | 
						||
    local render_transform = FWidgetTransform()
 | 
						||
    local first_point, last_point = mouse_tracks[1], mouse_tracks[#mouse_tracks]
 | 
						||
    local delta_x = last_point.X - first_point.X
 | 
						||
    local delta_y = last_point.Y - first_point.Y
 | 
						||
    local mask_length = (delta_x^2 + delta_y^2)^0.5  -- 轨迹长度,确定缩放参数
 | 
						||
    translation.X, translation.Y = first_point.X, first_point.Y  -- 第一个点确定图片唯一
 | 
						||
    scale.X, scale.Y = mask_length / 512, 1
 | 
						||
    render_transform.Scale = scale
 | 
						||
    render_transform.Translation = translation
 | 
						||
    render_transform.Angle = (math.atan(delta_y, delta_x) / (2 * math.pi)) * 360  -- 第一个点与最后一个点连线确定图片旋转角度
 | 
						||
 | 
						||
    widget:SetRenderTransform(render_transform)
 | 
						||
end
 | 
						||
 | 
						||
 | 
						||
-- 更新刀痕的材质
 | 
						||
local function UpdateCusMaskMaterial(widget, texture, mouse_track)
 | 
						||
    local transformed_tracks = TransformCurveToEndpointAxes(mouse_track)
 | 
						||
    local normalize_tracks = NormalizeCurveYToHalfRange(transformed_tracks)
 | 
						||
 | 
						||
    local offsets = {}
 | 
						||
    for _, track in ipairs(normalize_tracks) do
 | 
						||
        table.insert(offsets, track.Y)
 | 
						||
    end
 | 
						||
 | 
						||
    BusyGamePlayLibrary.UpdateTextureBuffer(texture, offsets)
 | 
						||
 | 
						||
    local material = widget:GetDynamicMaterial()
 | 
						||
    material:SetTextureParameterValue("Param", texture)
 | 
						||
    material:SetScalarParameterValue("VertexCount", #offsets)
 | 
						||
    material:SetScalarParameterValue("SourceWidth", 512)
 | 
						||
end
 | 
						||
 | 
						||
function PreCookCenterWidget:GetValidCutMaskWidget()
 | 
						||
    return self.ImgMask
 | 
						||
end
 | 
						||
 | 
						||
function PreCookCenterWidget:ResetAllCutMaskWidget()
 | 
						||
end
 | 
						||
 | 
						||
 | 
						||
function PreCookCenterWidget:Tick(geometry, delta_time)
 | 
						||
    -- 计算鼠标点被限定在该区域下的坐标
 | 
						||
 | 
						||
    local size = SlateBlueprintLibrary.GetLocalSize(geometry)
 | 
						||
    local cursor_pos = WidgetLayoutLibrary.GetMousePositionOnViewport(self)
 | 
						||
    local left_top = SlateBlueprintLibrary.GetLocalTopLeft(geometry)
 | 
						||
 | 
						||
    -- local fixed_x = math.min(math.max(cursor_pos.X - left_top.X, 0), size.X)
 | 
						||
    -- local fixed_y = math.min(math.max(cursor_pos.Y - left_top.Y, 0), size.Y)
 | 
						||
 | 
						||
    local fixed_x, fixed_y = cursor_pos.X - left_top.X, cursor_pos.Y - left_top.Y
 | 
						||
    if fixed_x < 0 or fixed_x > size.X or fixed_y < 0 or fixed_y > size.Y then return end
 | 
						||
 | 
						||
 | 
						||
    -- 更新鼠标移动轨迹
 | 
						||
    if self.is_pressed then
 | 
						||
        local last_point = self.mouse_tracks[#self.mouse_tracks]
 | 
						||
        if not last_point or math.abs(last_point.X - fixed_x) > 1 or math.abs(last_point.Y - fixed_y) > 1 then
 | 
						||
            table.insert(self.mouse_tracks, {X=fixed_x, Y=fixed_y, remain=0.5})
 | 
						||
        end
 | 
						||
    end
 | 
						||
 | 
						||
    -- 更新正在渲染的轨迹数据
 | 
						||
    local rendering_tracks = UpdateCutMaskData(self.rendering_tracks, delta_time)
 | 
						||
 | 
						||
    -- 绘制刀迹
 | 
						||
    for _, track in ipairs(rendering_tracks) do
 | 
						||
        if #track > 2 then
 | 
						||
            local widget = self:GetValidCutMaskWidget()
 | 
						||
            DrawCutMaskImage(widget, track)
 | 
						||
            -- UpdateCusMaskMaterial(widget, self.DataTexture, track)
 | 
						||
        end
 | 
						||
    end
 | 
						||
 | 
						||
    -- print("Ticking", #rendering_tracks)
 | 
						||
 | 
						||
    self.rendering_tracks = rendering_tracks
 | 
						||
end
 | 
						||
 | 
						||
 | 
						||
return Class(nil, nil, PreCookCenterWidget) |