Files
BusyRabbit/Content/Lua/HomeLand/UI/Hearth/Widgets/PreCookCenterWidget.lua
wyatt 4e7f89bd38 回退至简易刀光实现
刀光扭曲视觉表现不佳,笔直的刀光可能视觉感受更好,先不让材质扭曲,后续如果还需要实现扭曲效果,可以在这个版本的基础上继续研究
2025-09-01 01:14:17 +08:00

223 lines
7.5 KiB
Lua
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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