Loading...
Loading...
Compare original and translation side by side
AnimatorHumanoid:LoadAnimation()AnimatorHumanoid:LoadAnimation()HumanoidAnimatorAnimatorlocal character = player.Character or player.CharacterAdded:Wait()
local humanoid = character:WaitForChild("Humanoid")
local animator = humanoid:FindFirstChildOfClass("Animator")
or humanoid:WaitForChild("Animator")HumanoidAnimatorAnimatorlocal character = player.Character or player.CharacterAdded:Wait()
local humanoid = character:WaitForChild("Humanoid")
local animator = humanoid:FindFirstChildOfClass("Animator")
or humanoid:WaitForChild("Animator")-- 1. Create an Animation instance with the asset ID
local slashAnim = Instance.new("Animation")
slashAnim.AnimationId = "rbxassetid://123456789"
-- 2. Load it through the Animator (returns an AnimationTrack)
local slashTrack = animator:LoadAnimation(slashAnim)
-- 3. Play / Stop
slashTrack:Play()
-- Optional fade time and weight
slashTrack:Play(0.2) -- 0.2s fade-in
slashTrack:Stop(0.3) -- 0.3s fade-out
-- 4. Adjust speed at runtime
slashTrack:AdjustSpeed(1.5) -- 1.5x playback
slashTrack:AdjustWeight(0.8) -- 80% blend weight-- 1. 使用资源ID创建Animation实例
local slashAnim = Instance.new("Animation")
slashAnim.AnimationId = "rbxassetid://123456789"
-- 2. 通过Animator加载动画(返回AnimationTrack)
local slashTrack = animator:LoadAnimation(slashAnim)
-- 3. 播放 / 停止
slashTrack:Play()
-- 可选:设置淡入时间与权重
slashTrack:Play(0.2) -- 0.2秒淡入
slashTrack:Stop(0.3) -- 0.3秒淡出
-- 4. 运行时调整速度
slashTrack:AdjustSpeed(1.5) -- 1.5倍播放速度
slashTrack:AdjustWeight(0.8) -- 80%混合权重| Priority | Use Case |
|---|---|
| Breathing, idle sway |
| Walk, run, jump, fall |
| Attack, interact, emote |
| Higher-priority actions |
| Even higher-priority actions |
| Highest action tier |
| Internal Roblox (avoid overriding) |
slashTrack.Priority = Enum.AnimationPriority.Action| 优先级 | 使用场景 |
|---|---|
| 呼吸、 idle晃动 |
| 行走、奔跑、跳跃、坠落 |
| 攻击、交互、表情动作 |
| 更高优先级的动作 |
| 优先级更高的动作 |
| 最高优先级的动作 |
| Roblox内部动画(避免覆盖) |
slashTrack.Priority = Enum.AnimationPriority.ActionslashTrack:GetMarkerReachedSignal("HitFrame"):Connect(function(paramValue: string)
-- Spawn hitbox, play sound, emit particles, etc.
print("Hit frame reached!", paramValue)
end)slashTrack:GetMarkerReachedSignal("HitFrame"):Connect(function(paramValue: string)
-- 生成碰撞盒、播放音效、发射粒子等
print("击中帧触发!", paramValue)
end)local walkTrack = animator:LoadAnimation(walkAnim)
local runTrack = animator:LoadAnimation(runAnim)
walkTrack:Play(0.2)
-- Later, cross-fade to run
walkTrack:Stop(0.3)
runTrack:Play(0.3)local walkTrack = animator:LoadAnimation(walkAnim)
local runTrack = animator:LoadAnimation(runAnim)
walkTrack:Play(0.2)
-- 之后,交叉淡入到奔跑动画
walkTrack:Stop(0.3)
runTrack:Play(0.3)AnimationControllerHumanoidlocal model = workspace.DragonNPC
local animController = Instance.new("AnimationController")
animController.Parent = model
local flyAnim = Instance.new("Animation")
flyAnim.AnimationId = "rbxassetid://987654321"
local flyTrack = animController:LoadAnimation(flyAnim)
flyTrack.Looped = true
flyTrack:Play()HumanoidAnimationControllerlocal model = workspace.DragonNPC
local animController = Instance.new("AnimationController")
animController.Parent = model
local flyAnim = Instance.new("Animation")
flyAnim.AnimationId = "rbxassetid://987654321"
local flyTrack = animController:LoadAnimation(flyAnim)
flyTrack.Looped = true
flyTrack:Play()Motor6DPrimaryPartMotor6DPrimaryPart-- Typical setup for an animated NPC without Humanoid
local npc = Instance.new("Model")
npc.Name = "CrystalGolem"
local rootPart = Instance.new("Part")
rootPart.Name = "HumanoidRootPart" -- convention for animation rigs
rootPart.Anchored = false
rootPart.Parent = npc
npc.PrimaryPart = rootPart
local animController = Instance.new("AnimationController")
animController.Parent = npc
-- Add Motor6D joints, mesh parts, then load animations-- 无Humanoid的动画NPC典型设置
local npc = Instance.new("Model")
npc.Name = "CrystalGolem"
local rootPart = Instance.new("Part")
rootPart.Name = "HumanoidRootPart" -- 动画骨架的命名惯例
rootPart.Anchored = false
rootPart.Parent = npc
npc.PrimaryPart = rootPart
local animController = Instance.new("AnimationController")
animController.Parent = npc
-- 添加Motor6D关节、网格部件,然后加载动画| Property | Type | Description |
|---|---|---|
| | Particles emitted per second (0 = manual Emit()) |
| | How long each particle lives (seconds) |
| | Initial velocity (studs/second) |
| | Cone spread in X and Y (degrees) |
| | Size over particle lifetime |
| | Color over particle lifetime |
| | Transparency over particle lifetime |
| | Decal/image asset ID for particle appearance |
| | Rotation speed (degrees/second) |
| | Constant force (gravity = |
| | Air resistance (0 = none, higher = more drag) |
| | 0-1, additive blending (1 = fully additive/glowy) |
| | 0-1, how much scene lighting affects particles |
| | Render order offset toward/away from camera |
| | FacingCamera, VelocityParallel, etc. |
| 属性名称 | 类型 | 描述 |
|---|---|---|
| | 每秒发射的粒子数(0 = 手动调用Emit()) |
| | 每个粒子的生命周期(秒) |
| | 初始速度( studs/秒) |
| | X和Y轴的圆锥扩散角度(度) |
| | 粒子生命周期内的尺寸变化 |
| | 粒子生命周期内的颜色变化 |
| | 粒子生命周期内的透明度变化 |
| | 粒子外观的贴图/图像资源ID |
| | 旋转速度(度/秒) |
| | 恒定作用力(重力为 |
| | 空气阻力(0 = 无阻力,值越大阻力越强) |
| | 0-1, additive blending(1 = 完全叠加/发光) |
| | 0-1,场景光照对粒子的影响程度 |
| | 渲染顺序偏移(靠近/远离相机) |
| | 朝向相机、与速度平行等模式 |
-- Size: start at 1, peak at 2 at midlife, shrink to 0
local sizeSeq = NumberSequence.new({
NumberSequenceKeypoint.new(0, 1),
NumberSequenceKeypoint.new(0.5, 2),
NumberSequenceKeypoint.new(1, 0),
})
-- Color: orange to red
local colorSeq = ColorSequence.new({
ColorSequenceKeypoint.new(0, Color3.fromRGB(255, 170, 0)),
ColorSequenceKeypoint.new(1, Color3.fromRGB(200, 30, 0)),
})
-- Transparency: fade in then fade out
local transSeq = NumberSequence.new({
NumberSequenceKeypoint.new(0, 1),
NumberSequenceKeypoint.new(0.1, 0),
NumberSequenceKeypoint.new(0.8, 0),
NumberSequenceKeypoint.new(1, 1),
})-- 尺寸:初始为1,生命周期中期达到峰值2,最后缩小到0
local sizeSeq = NumberSequence.new({
NumberSequenceKeypoint.new(0, 1),
NumberSequenceKeypoint.new(0.5, 2),
NumberSequenceKeypoint.new(1, 0),
})
-- 颜色:从橙色过渡到红色
local colorSeq = ColorSequence.new({
ColorSequenceKeypoint.new(0, Color3.fromRGB(255, 170, 0)),
ColorSequenceKeypoint.new(1, Color3.fromRGB(200, 30, 0)),
})
-- 透明度:先淡入再淡出
local transSeq = NumberSequence.new({
NumberSequenceKeypoint.new(0, 1),
NumberSequenceKeypoint.new(0.1, 0),
NumberSequenceKeypoint.new(0.8, 0),
NumberSequenceKeypoint.new(1, 1),
})local fire = Instance.new("ParticleEmitter")
fire.Rate = 80
fire.Lifetime = NumberRange.new(0.4, 0.8)
fire.Speed = NumberRange.new(3, 6)
fire.SpreadAngle = Vector2.new(15, 15)
fire.Size = NumberSequence.new({
NumberSequenceKeypoint.new(0, 0.5),
NumberSequenceKeypoint.new(0.3, 1.5),
NumberSequenceKeypoint.new(1, 0),
})
fire.Color = ColorSequence.new({
ColorSequenceKeypoint.new(0, Color3.fromRGB(255, 220, 50)),
ColorSequenceKeypoint.new(0.4, Color3.fromRGB(255, 100, 0)),
ColorSequenceKeypoint.new(1, Color3.fromRGB(100, 20, 0)),
})
fire.Transparency = NumberSequence.new({
NumberSequenceKeypoint.new(0, 0.3),
NumberSequenceKeypoint.new(1, 1),
})
fire.LightEmission = 1
fire.Acceleration = Vector3.new(0, 4, 0)
fire.Texture = "rbxasset://textures/particles/fire_main.dds"
fire.Parent = somePartlocal fire = Instance.new("ParticleEmitter")
fire.Rate = 80
fire.Lifetime = NumberRange.new(0.4, 0.8)
fire.Speed = NumberRange.new(3, 6)
fire.SpreadAngle = Vector2.new(15, 15)
fire.Size = NumberSequence.new({
NumberSequenceKeypoint.new(0, 0.5),
NumberSequenceKeypoint.new(0.3, 1.5),
NumberSequenceKeypoint.new(1, 0),
})
fire.Color = ColorSequence.new({
ColorSequenceKeypoint.new(0, Color3.fromRGB(255, 220, 50)),
ColorSequenceKeypoint.new(0.4, Color3.fromRGB(255, 100, 0)),
ColorSequenceKeypoint.new(1, Color3.fromRGB(100, 20, 0)),
})
fire.Transparency = NumberSequence.new({
NumberSequenceKeypoint.new(0, 0.3),
NumberSequenceKeypoint.new(1, 1),
})
fire.LightEmission = 1
fire.Acceleration = Vector3.new(0, 4, 0)
fire.Texture = "rbxasset://textures/particles/fire_main.dds"
fire.Parent = somePartlocal smoke = Instance.new("ParticleEmitter")
smoke.Rate = 30
smoke.Lifetime = NumberRange.new(2, 4)
smoke.Speed = NumberRange.new(1, 3)
smoke.SpreadAngle = Vector2.new(30, 30)
smoke.Size = NumberSequence.new({
NumberSequenceKeypoint.new(0, 1),
NumberSequenceKeypoint.new(1, 5),
})
smoke.Color = ColorSequence.new(Color3.fromRGB(120, 120, 120))
smoke.Transparency = NumberSequence.new({
NumberSequenceKeypoint.new(0, 0.5),
NumberSequenceKeypoint.new(1, 1),
})
smoke.RotSpeed = NumberRange.new(-30, 30)
smoke.Acceleration = Vector3.new(0, 2, 0)
smoke.LightInfluence = 1
smoke.Parent = somePartlocal smoke = Instance.new("ParticleEmitter")
smoke.Rate = 30
smoke.Lifetime = NumberRange.new(2, 4)
smoke.Speed = NumberRange.new(1, 3)
smoke.SpreadAngle = Vector2.new(30, 30)
smoke.Size = NumberSequence.new({
NumberSequenceKeypoint.new(0, 1),
NumberSequenceKeypoint.new(1, 5),
})
smoke.Color = ColorSequence.new(Color3.fromRGB(120, 120, 120))
smoke.Transparency = NumberSequence.new({
NumberSequenceKeypoint.new(0, 0.5),
NumberSequenceKeypoint.new(1, 1),
})
smoke.RotSpeed = NumberRange.new(-30, 30)
smoke.Acceleration = Vector3.new(0, 2, 0)
smoke.LightInfluence = 1
smoke.Parent = somePartlocal sparkle = Instance.new("ParticleEmitter")
sparkle.Rate = 40
sparkle.Lifetime = NumberRange.new(0.5, 1.2)
sparkle.Speed = NumberRange.new(2, 5)
sparkle.SpreadAngle = Vector2.new(180, 180)
sparkle.Size = NumberSequence.new({
NumberSequenceKeypoint.new(0, 0.3),
NumberSequenceKeypoint.new(0.5, 0.6),
NumberSequenceKeypoint.new(1, 0),
})
sparkle.Color = ColorSequence.new({
ColorSequenceKeypoint.new(0, Color3.fromRGB(200, 200, 255)),
ColorSequenceKeypoint.new(1, Color3.fromRGB(100, 100, 255)),
})
sparkle.LightEmission = 1
sparkle.Texture = "rbxasset://textures/particles/sparkles_main.dds"
sparkle.Parent = somePartlocal sparkle = Instance.new("ParticleEmitter")
sparkle.Rate = 40
sparkle.Lifetime = NumberRange.new(0.5, 1.2)
sparkle.Speed = NumberRange.new(2, 5)
sparkle.SpreadAngle = Vector2.new(180, 180)
sparkle.Size = NumberSequence.new({
NumberSequenceKeypoint.new(0, 0.3),
NumberSequenceKeypoint.new(0.5, 0.6),
NumberSequenceKeypoint.new(1, 0),
})
sparkle.Color = ColorSequence.new({
ColorSequenceKeypoint.new(0, Color3.fromRGB(200, 200, 255)),
ColorSequenceKeypoint.new(1, Color3.fromRGB(100, 100, 255)),
})
sparkle.LightEmission = 1
sparkle.Texture = "rbxasset://textures/particles/sparkles_main.dds"
sparkle.Parent = somePartlocal rain = Instance.new("ParticleEmitter")
rain.Rate = 300
rain.Lifetime = NumberRange.new(0.8, 1.2)
rain.Speed = NumberRange.new(40, 60)
rain.SpreadAngle = Vector2.new(5, 5)
rain.Size = NumberSequence.new(0.05)
rain.Color = ColorSequence.new(Color3.fromRGB(180, 200, 220))
rain.Transparency = NumberSequence.new(0.4)
rain.Acceleration = Vector3.new(0, -80, 0)
rain.Drag = 0
rain.Orientation = Enum.ParticleOrientation.VelocityParallel
rain.Parent = largeCoverPart -- position above the play arealocal rain = Instance.new("ParticleEmitter")
rain.Rate = 300
rain.Lifetime = NumberRange.new(0.8, 1.2)
rain.Speed = NumberRange.new(40, 60)
rain.SpreadAngle = Vector2.new(5, 5)
rain.Size = NumberSequence.new(0.05)
rain.Color = ColorSequence.new(Color3.fromRGB(180, 200, 220))
rain.Transparency = NumberSequence.new(0.4)
rain.Acceleration = Vector3.new(0, -80, 0)
rain.Drag = 0
rain.Orientation = Enum.ParticleOrientation.VelocityParallel
rain.Parent = largeCoverPart -- 放置在游戏区域上方local snow = Instance.new("ParticleEmitter")
snow.Rate = 100
snow.Lifetime = NumberRange.new(4, 7)
snow.Speed = NumberRange.new(1, 3)
snow.SpreadAngle = Vector2.new(60, 60)
snow.Size = NumberSequence.new({
NumberSequenceKeypoint.new(0, 0.1),
NumberSequenceKeypoint.new(1, 0.15),
})
snow.Color = ColorSequence.new(Color3.new(1, 1, 1))
snow.Transparency = NumberSequence.new({
NumberSequenceKeypoint.new(0, 0),
NumberSequenceKeypoint.new(0.8, 0),
NumberSequenceKeypoint.new(1, 1),
})
snow.Acceleration = Vector3.new(0, -2, 0)
snow.RotSpeed = NumberRange.new(-60, 60)
snow.Drag = 3
snow.Parent = largeCoverPartlocal snow = Instance.new("ParticleEmitter")
snow.Rate = 100
snow.Lifetime = NumberRange.new(4, 7)
snow.Speed = NumberRange.new(1, 3)
snow.SpreadAngle = Vector2.new(60, 60)
snow.Size = NumberSequence.new({
NumberSequenceKeypoint.new(0, 0.1),
NumberSequenceKeypoint.new(1, 0.15),
})
snow.Color = ColorSequence.new(Color3.new(1, 1, 1))
snow.Transparency = NumberSequence.new({
NumberSequenceKeypoint.new(0, 0),
NumberSequenceKeypoint.new(0.8, 0),
NumberSequenceKeypoint.new(1, 1),
})
snow.Acceleration = Vector3.new(0, -2, 0)
snow.RotSpeed = NumberRange.new(-60, 60)
snow.Drag = 3
snow.Parent = largeCoverPartlocal aura = Instance.new("ParticleEmitter")
aura.Rate = 25
aura.Lifetime = NumberRange.new(1, 2)
aura.Speed = NumberRange.new(0.5, 1.5)
aura.SpreadAngle = Vector2.new(180, 180)
aura.Size = NumberSequence.new({
NumberSequenceKeypoint.new(0, 0),
NumberSequenceKeypoint.new(0.3, 0.8),
NumberSequenceKeypoint.new(1, 0),
})
aura.Color = ColorSequence.new({
ColorSequenceKeypoint.new(0, Color3.fromRGB(100, 0, 255)),
ColorSequenceKeypoint.new(1, Color3.fromRGB(200, 50, 255)),
})
aura.Transparency = NumberSequence.new({
NumberSequenceKeypoint.new(0, 1),
NumberSequenceKeypoint.new(0.2, 0.2),
NumberSequenceKeypoint.new(1, 1),
})
aura.LightEmission = 1
aura.RotSpeed = NumberRange.new(-90, 90)
aura.Drag = 5
aura.Parent = character.HumanoidRootPartlocal aura = Instance.new("ParticleEmitter")
aura.Rate = 25
aura.Lifetime = NumberRange.new(1, 2)
aura.Speed = NumberRange.new(0.5, 1.5)
aura.SpreadAngle = Vector2.new(180, 180)
aura.Size = NumberSequence.new({
NumberSequenceKeypoint.new(0, 0),
NumberSequenceKeypoint.new(0.3, 0.8),
NumberSequenceKeypoint.new(1, 0),
})
aura.Color = ColorSequence.new({
ColorSequenceKeypoint.new(0, Color3.fromRGB(100, 0, 255)),
ColorSequenceKeypoint.new(1, Color3.fromRGB(200, 50, 255)),
})
aura.Transparency = NumberSequence.new({
NumberSequenceKeypoint.new(0, 1),
NumberSequenceKeypoint.new(0.2, 0.2),
NumberSequenceKeypoint.new(1, 1),
})
aura.LightEmission = 1
aura.RotSpeed = NumberRange.new(-90, 90)
aura.Drag = 5
aura.Parent = character.HumanoidRootPartBeamAttachment-- Setup: two parts with attachments
local att0 = Instance.new("Attachment")
att0.Parent = partA
local att1 = Instance.new("Attachment")
att1.Parent = partB
local beam = Instance.new("Beam")
beam.Attachment0 = att0
beam.Attachment1 = att1
beam.Width0 = 0.5
beam.Width1 = 0.5
beam.Color = ColorSequence.new(Color3.fromRGB(0, 150, 255))
beam.Transparency = NumberSequence.new({
NumberSequenceKeypoint.new(0, 0),
NumberSequenceKeypoint.new(1, 0.5),
})
beam.LightEmission = 1
beam.FaceCamera = true
beam.Segments = 20 -- more segments = smoother curves
beam.CurveSize0 = 2 -- bend near Attachment0
beam.CurveSize1 = -2 -- bend near Attachment1
beam.TextureLength = 1
beam.TextureSpeed = 1 -- scrolling texture
beam.Texture = "rbxassetid://123456789"
beam.Parent = partABeamAttachment-- 设置:两个带有Attachment的部件
local att0 = Instance.new("Attachment")
att0.Parent = partA
local att1 = Instance.new("Attachment")
att1.Parent = partB
local beam = Instance.new("Beam")
beam.Attachment0 = att0
beam.Attachment1 = att1
beam.Width0 = 0.5
beam.Width1 = 0.5
beam.Color = ColorSequence.new(Color3.fromRGB(0, 150, 255))
beam.Transparency = NumberSequence.new({
NumberSequenceKeypoint.new(0, 0),
NumberSequenceKeypoint.new(1, 0.5),
})
beam.LightEmission = 1
beam.FaceCamera = true
beam.Segments = 20 -- 分段数越多,曲线越平滑
beam.CurveSize0 = 2 -- 在Attachment0附近弯曲
beam.CurveSize1 = -2 -- 在Attachment1附近弯曲
beam.TextureLength = 1
beam.TextureSpeed = 1 -- 纹理滚动速度
beam.Texture = "rbxassetid://123456789"
beam.Parent = partA| Property | Description |
|---|---|
| Start and end points |
| Width at each attachment (studs) |
| |
| |
| Bezier curve magnitude at each end |
| Number of straight segments (more = smoother curves) |
| Always faces the camera for billboard effect |
| Scrolls the texture along the beam |
| Additive blending for glow |
| 属性名称 | 描述 |
|---|---|
| 起点和终点 |
| 每个Attachment处的宽度(studs) |
| 光束长度方向的 |
| 光束长度方向的 |
| 两端的贝塞尔曲线幅度 |
| 直线分段数(越多曲线越平滑) |
| 始终朝向相机,实现公告板效果 |
| 沿光束滚动纹理 |
| 叠加混合实现发光效果 |
TrailAttachmentlocal part = workspace.Sword.Blade
local att0 = Instance.new("Attachment")
att0.Position = Vector3.new(0, 0, -2) -- base of blade
att0.Parent = part
local att1 = Instance.new("Attachment")
att1.Position = Vector3.new(0, 0, 2) -- tip of blade
att1.Parent = part
local trail = Instance.new("Trail")
trail.Attachment0 = att0
trail.Attachment1 = att1
trail.Lifetime = 0.3 -- how long segments persist
trail.MinLength = 0.05 -- minimum distance before new segment
trail.FaceCamera = true
trail.Color = ColorSequence.new({
ColorSequenceKeypoint.new(0, Color3.fromRGB(255, 255, 255)),
ColorSequenceKeypoint.new(1, Color3.fromRGB(100, 100, 255)),
})
trail.Transparency = NumberSequence.new({
NumberSequenceKeypoint.new(0, 0),
NumberSequenceKeypoint.new(1, 1),
})
trail.LightEmission = 0.8
trail.WidthScale = NumberSequence.new(1)
trail.Parent = partTrailAttachmentlocal part = workspace.Sword.Blade
local att0 = Instance.new("Attachment")
att0.Position = Vector3.new(0, 0, -2) -- 剑柄位置
att0.Parent = part
local att1 = Instance.new("Attachment")
att1.Position = Vector3.new(0, 0, 2) -- 剑尖位置
att1.Parent = part
local trail = Instance.new("Trail")
trail.Attachment0 = att0
trail.Attachment1 = att1
trail.Lifetime = 0.3 -- 轨迹片段的持续时间
trail.MinLength = 0.05 -- 生成新片段的最小移动距离
trail.FaceCamera = true
trail.Color = ColorSequence.new({
ColorSequenceKeypoint.new(0, Color3.fromRGB(255, 255, 255)),
ColorSequenceKeypoint.new(1, Color3.fromRGB(100, 100, 255)),
})
trail.Transparency = NumberSequence.new({
NumberSequenceKeypoint.new(0, 0),
NumberSequenceKeypoint.new(1, 1),
})
trail.LightEmission = 0.8
trail.WidthScale = NumberSequence.new(1)
trail.Parent = partBeamTrailLifetimeBeamCurveSizeBeamSegmentsCurveSize0/1BeamTrailLifetimeBeamCurveSizeBeamSegmentsCurveSize0/1PartModel:Destroy()ParticleEmitterTrailBeamAttachmentDebrislocal Debris = game:GetService("Debris")
local function destroyWithGrace(effect: Instance, parent: Instance, gracePeriod: number)
-- Create a temporary invisible holder
local holder = Instance.new("Part")
holder.Anchored = true
holder.CanCollide = false
holder.Transparency = 1
holder.Size = Vector3.one
holder.Parent = workspace
-- Reparent the effect so it survives the original parent
effect.Parent = holder
-- Destroy the original parent (effect is now safe)
parent:Destroy()
-- Debris cleans up the holder + effect after the grace period
Debris:AddItem(holder, gracePeriod)
end
-- Example: Trail with 1s lifetime - give it 1.1s to fade out cleanly
local trail = -- ... setup trail on a sword part ...
destroyWithGrace(trail, swordPart, 1.1)TrailDebris:AddItem(holder, trail.Lifetime + 0.1)PartModel:Destroy()ParticleEmitterTrailBeamAttachmentDebrislocal Debris = game:GetService("Debris")
local function destroyWithGrace(effect: Instance, parent: Instance, gracePeriod: number)
-- 创建临时不可见容器
local holder = Instance.new("Part")
holder.Anchored = true
holder.CanCollide = false
holder.Transparency = 1
holder.Size = Vector3.one
holder.Parent = workspace
-- 将效果重新挂载,使其在原父对象销毁后存活
effect.Parent = holder
-- 销毁原父对象(效果现在安全了)
parent:Destroy()
-- 等待宽限期后,Debris清理容器和效果
Debris:AddItem(holder, gracePeriod)
end
-- 示例:轨迹生命周期为1秒 - 给1.1秒让它完全淡出
local trail = -- ... 在剑部件上设置轨迹 ...
destroyWithGrace(trail, swordPart, 1.1)TrailDebris:AddItem(holder, trail.Lifetime + 0.1)HighlightBasePartModelHighlightBasePartModellocal highlight = Instance.new("Highlight")
highlight.Adornee = targetPart
highlight.FillColor = Color3.fromRGB(255, 50, 50)
highlight.FillTransparency = 0.3
highlight.OutlineColor = Color3.new(1, 1, 1)
highlight.Parent = targetPartlocal highlight = Instance.new("Highlight")
highlight.Adornee = targetPart
highlight.FillColor = Color3.fromRGB(255, 50, 50)
highlight.FillTransparency = 0.3
highlight.OutlineColor = Color3.new(1, 1, 1)
highlight.Parent = targetPart| Property | Type | Default | Description |
|---|---|---|---|
| Instance | - | The |
| Enum.HighlightDepthMode | | |
| boolean | | Toggle visibility |
| Color3 | | Interior overlay color |
| number | | 0 = opaque, 1 = invisible |
| Color3 | | Edge outline color |
| number | | 0 = opaque, 1 = invisible |
| 属性名称 | 类型 | 默认值 | 描述 |
|---|---|---|---|
| Instance | - | 要高亮的 |
| Enum.HighlightDepthMode | | |
| boolean | | 切换可见性 |
| Color3 | | 内部填充颜色 |
| number | | 0 = 不透明,1 = 完全透明 |
| Color3 | | 边缘轮廓颜色 |
| number | | 0 = 不透明,1 = 完全透明 |
local function addTeamHighlight(character: Model, teamColor: Color3)
local hl = Instance.new("Highlight")
hl.Adornee = character
hl.FillColor = teamColor
hl.FillTransparency = 0.6
hl.OutlineColor = teamColor
hl.DepthMode = Enum.HighlightDepthMode.AlwaysOnTop
hl.Parent = character
endlocal function addTeamHighlight(character: Model, teamColor: Color3)
local hl = Instance.new("Highlight")
hl.Adornee = character
hl.FillColor = teamColor
hl.FillTransparency = 0.6
hl.OutlineColor = teamColor
hl.DepthMode = Enum.HighlightDepthMode.AlwaysOnTop
hl.Parent = character
end:Destroy()Enabled = falseHighlightAdornee:Destroy()Enabled = falseAdorneeHighlightlocal function attachHighlight(adornee: Instance): Highlight
local hl = Instance.new("Highlight")
hl.Adornee = adornee
hl.Parent = adornee
adornee.AncestryChanged:Connect(function()
if not adornee:IsDescendantOf(game) then
hl:Destroy()
end
end)
return hl
endlocal function attachHighlight(adornee: Instance): Highlight
local hl = Instance.new("Highlight")
hl.Adornee = adornee
hl.Parent = adornee
adornee.AncestryChanged:Connect(function()
if not adornee:IsDescendantOf(game) then
hl:Destroy()
end
end)
return hl
endTweenServiceTweenServicelocal TweenService = game:GetService("TweenService")
local info = TweenInfo.new(
0.5, -- Duration (seconds)
Enum.EasingStyle.Quad, -- Easing style
Enum.EasingDirection.Out, -- Easing direction
0, -- RepeatCount (0 = no repeat, -1 = infinite)
false, -- Reverses (plays backward after forward)
0 -- DelayTime (seconds before starting)
)local TweenService = game:GetService("TweenService")
local info = TweenInfo.new(
0.5, -- 持续时间(秒)
Enum.EasingStyle.Quad, -- 缓动风格
Enum.EasingDirection.Out, -- 缓动方向
0, -- 重复次数(0 = 不重复,-1 = 无限重复)
false, -- 是否反向播放(正向播放后反向)
0 -- 延迟时间(秒)
)| Style | Feel |
|---|---|
| Constant speed, mechanical |
| Gentle acceleration/deceleration |
| Stronger ease |
| Even stronger |
| Smooth, organic |
| Overshoots then settles |
| Bounces at the end |
| Springy overshoot |
| Very sharp acceleration |
| 风格 | 效果感受 |
|---|---|
| 匀速,机械感强 |
| 平缓加速/减速 |
| 更强的缓动效果 |
| 缓动效果更明显 |
| 平滑,自然感强 |
| 先超出目标再回弹 |
| 结束时弹跳效果 |
| 弹性回弹效果 |
| 急剧加速 |
-- Flash on hit: turn white then revert
local function flashPart(part: BasePart, originalColor: Color3)
part.Color = Color3.new(1, 1, 1) -- instant white
local tweenBack = TweenService:Create(part, TweenInfo.new(0.3, Enum.EasingStyle.Quad), {
Color = originalColor,
})
tweenBack:Play()
end-- Pulse effect: scale up then back
local function pulse(part: BasePart)
local originalSize = part.Size
local tweenGrow = TweenService:Create(part, TweenInfo.new(0.15, Enum.EasingStyle.Back, Enum.EasingDirection.Out), {
Size = originalSize * 1.3,
})
local tweenShrink = TweenService:Create(part, TweenInfo.new(0.2, Enum.EasingStyle.Quad, Enum.EasingDirection.In), {
Size = originalSize,
})
tweenGrow:Play()
tweenGrow.Completed:Connect(function()
tweenShrink:Play()
end)
end-- Grow and fade out (explosion ring)
local function expandAndFade(part: BasePart)
local tween = TweenService:Create(part, TweenInfo.new(0.6, Enum.EasingStyle.Quad, Enum.EasingDirection.Out), {
Size = part.Size * 5,
Transparency = 1,
})
tween:Play()
tween.Completed:Connect(function()
part:Destroy()
end)
end-- Color transition (damage indicator)
local function colorTransition(part: BasePart, targetColor: Color3, duration: number)
local tween = TweenService:Create(part, TweenInfo.new(duration, Enum.EasingStyle.Sine), {
Color = targetColor,
})
tween:Play()
end-- 击中闪光:瞬间变白然后恢复原颜色
local function flashPart(part: BasePart, originalColor: Color3)
part.Color = Color3.new(1, 1, 1) -- 瞬间变白
local tweenBack = TweenService:Create(part, TweenInfo.new(0.3, Enum.EasingStyle.Quad), {
Color = originalColor,
})
tweenBack:Play()
end-- 脉冲效果:放大然后缩小
local function pulse(part: BasePart)
local originalSize = part.Size
local tweenGrow = TweenService:Create(part, TweenInfo.new(0.15, Enum.EasingStyle.Back, Enum.EasingDirection.Out), {
Size = originalSize * 1.3,
})
local tweenShrink = TweenService:Create(part, TweenInfo.new(0.2, Enum.EasingStyle.Quad, Enum.EasingDirection.In), {
Size = originalSize,
})
tweenGrow:Play()
tweenGrow.Completed:Connect(function()
tweenShrink:Play()
end)
end-- 扩大并淡出(爆炸环)
local function expandAndFade(part: BasePart)
local tween = TweenService:Create(part, TweenInfo.new(0.6, Enum.EasingStyle.Quad, Enum.EasingDirection.Out), {
Size = part.Size * 5,
Transparency = 1,
})
tween:Play()
tween.Completed:Connect(function()
part:Destroy()
end)
end-- 颜色过渡(伤害指示器)
local function colorTransition(part: BasePart, targetColor: Color3, duration: number)
local tween = TweenService:Create(part, TweenInfo.new(duration, Enum.EasingStyle.Sine), {
Color = targetColor,
})
tween:Play()
endCompletedlocal function chainedEffect(part: BasePart)
local step1 = TweenService:Create(part, TweenInfo.new(0.2), { Transparency = 0.5 })
local step2 = TweenService:Create(part, TweenInfo.new(0.3), { Size = part.Size * 2 })
local step3 = TweenService:Create(part, TweenInfo.new(0.2), { Transparency = 1 })
step1:Play()
step1.Completed:Connect(function()
step2:Play()
end)
step2.Completed:Connect(function()
step3:Play()
end)
step3.Completed:Connect(function()
part:Destroy()
end)
endCompletedlocal function chainedEffect(part: BasePart)
local step1 = TweenService:Create(part, TweenInfo.new(0.2), { Transparency = 0.5 })
local step2 = TweenService:Create(part, TweenInfo.new(0.3), { Size = part.Size * 2 })
local step3 = TweenService:Create(part, TweenInfo.new(0.2), { Transparency = 1 })
step1:Play()
step1.Completed:Connect(function()
step2:Play()
end)
step2.Completed:Connect(function()
step3:Play()
end)
step3.Completed:Connect(function()
part:Destroy()
end)
end-- PointLight: omnidirectional, good for torches, explosions
local pointLight = Instance.new("PointLight")
pointLight.Brightness = 2
pointLight.Color = Color3.fromRGB(255, 180, 50)
pointLight.Range = 20
pointLight.Shadows = true
pointLight.Parent = torchPart
-- SpotLight: directional cone, good for flashlights, spotlights
local spotLight = Instance.new("SpotLight")
spotLight.Brightness = 3
spotLight.Color = Color3.new(1, 1, 1)
spotLight.Range = 40
spotLight.Angle = 30 -- cone half-angle in degrees
spotLight.Face = Enum.NormalId.Front
spotLight.Parent = flashlightPart
-- SurfaceLight: emits from a surface, good for screens, signs
local surfLight = Instance.new("SurfaceLight")
surfLight.Brightness = 1
surfLight.Color = Color3.fromRGB(0, 200, 255)
surfLight.Range = 10
surfLight.Face = Enum.NormalId.Front
surfLight.Parent = screenPart-- PointLight:全向光源,适合火把、爆炸效果
local pointLight = Instance.new("PointLight")
pointLight.Brightness = 2
pointLight.Color = Color3.fromRGB(255, 180, 50)
pointLight.Range = 20
pointLight.Shadows = true
pointLight.Parent = torchPart
-- SpotLight:定向锥形光源,适合手电筒、聚光灯
local spotLight = Instance.new("SpotLight")
spotLight.Brightness = 3
spotLight.Color = Color3.new(1, 1, 1)
spotLight.Range = 40
spotLight.Angle = 30 -- 锥形半角(度)
spotLight.Face = Enum.NormalId.Front
spotLight.Parent = flashlightPart
-- SurfaceLight:从表面发光,适合屏幕、标牌
local surfLight = Instance.new("SurfaceLight")
surfLight.Brightness = 1
surfLight.Color = Color3.fromRGB(0, 200, 255)
surfLight.Range = 10
surfLight.Face = Enum.NormalId.Front
surfLight.Parent = screenPartLightingCameraLightingCameralocal atmo = Instance.new("Atmosphere")
atmo.Density = 0.3 -- 0-1, how thick the atmosphere is
atmo.Offset = 0.25 -- shifts haze up/down
atmo.Color = Color3.fromRGB(200, 210, 230) -- scatter color
atmo.Decay = Color3.fromRGB(120, 140, 170) -- far-away color
atmo.Glare = 0.2 -- sun glare intensity
atmo.Haze = 1.5 -- haze amount
atmo.Parent = game.Lightinglocal atmo = Instance.new("Atmosphere")
atmo.Density = 0.3 -- 0-1,大气厚度
atmo.Offset = 0.25 -- 上下移动雾的位置
atmo.Color = Color3.fromRGB(200, 210, 230) -- 散射颜色
atmo.Decay = Color3.fromRGB(120, 140, 170) -- 远处颜色
atmo.Glare = 0.2 -- 太阳眩光强度
atmo.Haze = 1.5 -- 雾量
atmo.Parent = game.Lightinglocal cc = Instance.new("ColorCorrectionEffect")
cc.Brightness = 0.05 -- -1 to 1
cc.Contrast = 0.1 -- -1 to 1
cc.Saturation = 0.15 -- -1 to 1
cc.TintColor = Color3.new(1, 0.95, 0.9) -- warm tint
cc.Parent = game.Lightinglocal cc = Instance.new("ColorCorrectionEffect")
cc.Brightness = 0.05 -- -1到1
cc.Contrast = 0.1 -- -1到1
cc.Saturation = 0.15 -- -1到1
cc.TintColor = Color3.new(1, 0.95, 0.9) -- 暖色调
cc.Parent = game.Lightinglocal bloom = Instance.new("BloomEffect")
bloom.Intensity = 0.8 -- glow strength
bloom.Size = 24 -- glow spread (pixels)
bloom.Threshold = 1.2 -- brightness threshold to bloom
bloom.Parent = game.Lightinglocal bloom = Instance.new("BloomEffect")
bloom.Intensity = 0.8 -- 发光强度
bloom.Size = 24 -- 发光扩散范围(像素)
bloom.Threshold = 1.2 -- 触发发光的亮度阈值
bloom.Parent = game.Lightinglocal dof = Instance.new("DepthOfFieldEffect")
dof.FarIntensity = 0.3 -- blur intensity far from focus
dof.FocusDistance = 30 -- distance in studs to focus point
dof.InFocusRadius = 20 -- radius of sharp area
dof.NearIntensity = 0.2 -- blur intensity near camera
dof.Parent = game.Lightinglocal dof = Instance.new("DepthOfFieldEffect")
dof.FarIntensity = 0.3 -- 远离焦点区域的模糊强度
dof.FocusDistance = 30 -- 焦点距离(studs)
dof.InFocusRadius = 20 -- 清晰区域半径
dof.NearIntensity = 0.2 -- 靠近相机区域的模糊强度
dof.Parent = game.Lightinglocal sunRays = Instance.new("SunRaysEffect")
sunRays.Intensity = 0.15 -- ray visibility
sunRays.Spread = 0.8 -- how far rays extend
sunRays.Parent = game.Lightinglocal sunRays = Instance.new("SunRaysEffect")
sunRays.Intensity = 0.15 -- 射线可见度
sunRays.Spread = 0.8 -- 射线延伸距离
sunRays.Parent = game.Lightinglocal lighting = game.Lighting
-- Daytime bright
lighting.ClockTime = 14
lighting.Brightness = 2
lighting.Ambient = Color3.fromRGB(140, 140, 140)
lighting.OutdoorAmbient = Color3.fromRGB(130, 130, 130)
-- Nighttime spooky
lighting.ClockTime = 0
lighting.Brightness = 0.5
lighting.Ambient = Color3.fromRGB(20, 20, 40)
lighting.OutdoorAmbient = Color3.fromRGB(10, 10, 30)
lighting.FogEnd = 200
lighting.FogColor = Color3.fromRGB(15, 15, 30)local lighting = game.Lighting
-- 白天明亮效果
lighting.ClockTime = 14
lighting.Brightness = 2
lighting.Ambient = Color3.fromRGB(140, 140, 140)
lighting.OutdoorAmbient = Color3.fromRGB(130, 130, 130)
-- 夜晚恐怖效果
lighting.ClockTime = 0
lighting.Brightness = 0.5
lighting.Ambient = Color3.fromRGB(20, 20, 40)
lighting.OutdoorAmbient = Color3.fromRGB(10, 10, 30)
lighting.FogEnd = 200
lighting.FogColor = Color3.fromRGB(15, 15, 30)-- Sync a footstep sound to walk animation markers
walkTrack:GetMarkerReachedSignal("Footstep"):Connect(function()
local footstep = Instance.new("Sound")
footstep.SoundId = "rbxassetid://112233445"
footstep.Volume = 0.5
footstep.PlaybackSpeed = 0.9 + math.random() * 0.2 -- slight variation
footstep.Parent = character.HumanoidRootPart
footstep:Play()
footstep.Ended:Connect(function()
footstep:Destroy()
end)
end)-- 将脚步声与行走动画标记同步
walkTrack:GetMarkerReachedSignal("Footstep"):Connect(function()
local footstep = Instance.new("Sound")
footstep.SoundId = "rbxassetid://112233445"
footstep.Volume = 0.5
footstep.PlaybackSpeed = 0.9 + math.random() * 0.2 -- 轻微随机变化
footstep.Parent = character.HumanoidRootPart
footstep:Play()
footstep.Ended:Connect(function()
footstep:Destroy()
end)
end)local camera = workspace.CurrentCamera
local RunService = game:GetService("RunService")
local function shakeCamera(intensity: number, duration: number)
local elapsed = 0
local originalCFrame = camera.CFrame
local connection: RBXScriptConnection
connection = RunService.RenderStepped:Connect(function(dt: number)
elapsed += dt
if elapsed >= duration then
connection:Disconnect()
-- Camera returns to normal since CameraType is usually
-- "Custom" (player-controlled)
return
end
local progress = 1 - (elapsed / duration) -- decay over time
local shakeX = (math.random() - 0.5) * 2 * intensity * progress
local shakeY = (math.random() - 0.5) * 2 * intensity * progress
local shakeZ = (math.random() - 0.5) * 2 * intensity * progress
camera.CFrame = camera.CFrame * CFrame.new(shakeX, shakeY, shakeZ)
end)
end
-- Usage: moderate shake for 0.3 seconds
shakeCamera(0.5, 0.3)local camera = workspace.CurrentCamera
local RunService = game:GetService("RunService")
local function shakeCamera(intensity: number, duration: number)
local elapsed = 0
local originalCFrame = camera.CFrame
local connection: RBXScriptConnection
connection = RunService.RenderStepped:Connect(function(dt: number)
elapsed += dt
if elapsed >= duration then
connection:Disconnect()
-- 由于CameraType通常为"Custom"(玩家控制),相机会自动恢复正常
return
end
local progress = 1 - (elapsed / duration) -- 随时间衰减
local shakeX = (math.random() - 0.5) * 2 * intensity * progress
local shakeY = (math.random() - 0.5) * 2 * intensity * progress
local shakeZ = (math.random() - 0.5) * 2 * intensity * progress
camera.CFrame = camera.CFrame * CFrame.new(shakeX, shakeY, shakeZ)
end)
end
-- 使用:中等强度震动0.3秒
shakeCamera(0.5, 0.3)local TweenService = game:GetService("TweenService")
local camera = workspace.CurrentCamera
local function zoomCamera(targetFOV: number, duration: number)
local tween = TweenService:Create(camera, TweenInfo.new(duration, Enum.EasingStyle.Quad, Enum.EasingDirection.Out), {
FieldOfView = targetFOV,
})
tween:Play()
return tween
end
-- Zoom in for dramatic moment
local zoomIn = zoomCamera(40, 0.5)
zoomIn.Completed:Connect(function()
task.wait(1)
zoomCamera(70, 0.8) -- zoom back to normal
end)local TweenService = game:GetService("TweenService")
local camera = workspace.CurrentCamera
local function zoomCamera(targetFOV: number, duration: number)
local tween = TweenService:Create(camera, TweenInfo.new(duration, Enum.EasingStyle.Quad, Enum.EasingDirection.Out), {
FieldOfView = targetFOV,
})
tween:Play()
return tween
end
-- 放大营造戏剧性时刻
local zoomIn = zoomCamera(40, 0.5)
zoomIn.Completed:Connect(function()
task.wait(1)
zoomCamera(70, 0.8) -- 恢复正常缩放
end)local function focusOnPoint(targetCFrame: CFrame, duration: number)
camera.CameraType = Enum.CameraType.Scriptable
local startCFrame = camera.CFrame
local elapsed = 0
local connection: RBXScriptConnection
connection = RunService.RenderStepped:Connect(function(dt: number)
elapsed += dt
local alpha = math.clamp(elapsed / duration, 0, 1)
-- Smooth step for natural feel
local smoothAlpha = alpha * alpha * (3 - 2 * alpha)
camera.CFrame = startCFrame:Lerp(targetCFrame, smoothAlpha)
if alpha >= 1 then
connection:Disconnect()
end
end)
endlocal function focusOnPoint(targetCFrame: CFrame, duration: number)
camera.CameraType = Enum.CameraType.Scriptable
local startCFrame = camera.CFrame
local elapsed = 0
local connection: RBXScriptConnection
connection = RunService.RenderStepped:Connect(function(dt: number)
elapsed += dt
local alpha = math.clamp(elapsed / duration, 0, 1)
-- 平滑步进实现自然效果
local smoothAlpha = alpha * alpha * (3 - 2 * alpha)
camera.CFrame = startCFrame:Lerp(targetCFrame, smoothAlpha)
if alpha >= 1 then
connection:Disconnect()
end
end)
endtype CutsceneWaypoint = {
cframe: CFrame,
duration: number,
easingStyle: Enum.EasingStyle?,
holdTime: number?, -- pause at this point before moving on
}
local function playCutscene(waypoints: { CutsceneWaypoint })
camera.CameraType = Enum.CameraType.Scriptable
for i, waypoint in waypoints do
local style = waypoint.easingStyle or Enum.EasingStyle.Quad
local info = TweenInfo.new(waypoint.duration, style, Enum.EasingDirection.InOut)
local tween = TweenService:Create(camera, info, {
CFrame = waypoint.cframe,
})
tween:Play()
tween.Completed:Wait()
if waypoint.holdTime and waypoint.holdTime > 0 then
task.wait(waypoint.holdTime)
end
end
-- Return camera control to player
camera.CameraType = Enum.CameraType.Custom
end
-- Usage
playCutscene({
{ cframe = CFrame.new(0, 50, 0) * CFrame.Angles(math.rad(-90), 0, 0), duration = 2, holdTime = 1 },
{ cframe = CFrame.new(20, 10, 20) * CFrame.lookAt(Vector3.new(20, 10, 20), Vector3.zero), duration = 3 },
{ cframe = camera.CFrame, duration = 1.5, easingStyle = Enum.EasingStyle.Sine },
})type CutsceneWaypoint = {
cframe: CFrame,
duration: number,
easingStyle: Enum.EasingStyle?,
holdTime: number?, -- 在该点暂停后再继续
}
local function playCutscene(waypoints: { CutsceneWaypoint })
camera.CameraType = Enum.CameraType.Scriptable
for i, waypoint in waypoints do
local style = waypoint.easingStyle or Enum.EasingStyle.Quad
local info = TweenInfo.new(waypoint.duration, style, Enum.EasingDirection.InOut)
local tween = TweenService:Create(camera, info, {
CFrame = waypoint.cframe,
})
tween:Play()
tween.Completed:Wait()
if waypoint.holdTime and waypoint.holdTime > 0 then
task.wait(waypoint.holdTime)
end
end
-- 将相机控制权交还给玩家
camera.CameraType = Enum.CameraType.Custom
end
-- 使用示例
playCutscene({
{ cframe = CFrame.new(0, 50, 0) * CFrame.Angles(math.rad(-90), 0, 0), duration = 2, holdTime = 1 },
{ cframe = CFrame.new(20, 10, 20) * CFrame.lookAt(Vector3.new(20, 10, 20), Vector3.zero), duration = 3 },
{ cframe = camera.CFrame, duration = 1.5, easingStyle = Enum.EasingStyle.Sine },
})SegmentsEndedSegmentsEndedlocal function getQualityLevel(): string
local quality = UserSettings().GameSettings.SavedQualityLevel
-- quality is Enum.SavedQualitySetting or an int 1-10
if quality == Enum.SavedQualitySetting.Automatic then
-- Use a heuristic: check current graphics quality
local level = settings().Rendering.QualityLevel
if level <= 3 then return "low"
elseif level <= 6 then return "medium"
else return "high" end
end
local numQuality = quality.Value
if numQuality <= 3 then return "low"
elseif numQuality <= 6 then return "medium"
else return "high" end
end
local function applyQualitySettings()
local level = getQualityLevel()
if level == "low" then
-- Disable post-processing
for _, effect in game.Lighting:GetChildren() do
if effect:IsA("PostEffect") then
effect.Enabled = false
end
end
-- Reduce particle rates
-- Disable shadows on dynamic lights
end
endlocal function getQualityLevel(): string
local quality = UserSettings().GameSettings.SavedQualityLevel
-- quality是Enum.SavedQualitySetting或1-10的整数
if quality == Enum.SavedQualitySetting.Automatic then
-- 使用启发式方法:检查当前图形质量
local level = settings().Rendering.QualityLevel
if level <= 3 then return "low"
elseif level <= 6 then return "medium"
else return "high" end
end
local numQuality = quality.Value
if numQuality <= 3 then return "low"
elseif numQuality <= 6 then return "medium"
else return "high" end
end
local function applyQualitySettings()
local level = getQualityLevel()
if level == "low" then
-- 禁用后期处理
for _, effect in game.Lighting:GetChildren() do
if effect:IsA("PostEffect") then
effect.Enabled = false
end
end
-- 降低粒子发射率
-- 禁用动态光源的阴影
end
endlocal VFXPool = {}
VFXPool.__index = VFXPool
function VFXPool.new(template: Instance, poolSize: number): typeof(setmetatable({}, VFXPool))
local self = setmetatable({}, VFXPool)
self._pool = table.create(poolSize)
self._available = table.create(poolSize)
for i = 1, poolSize do
local clone = template:Clone()
clone.Parent = workspace.VFXFolder
-- Disable all emitters
for _, emitter in clone:GetDescendants() do
if emitter:IsA("ParticleEmitter") then
emitter.Enabled = false
end
end
self._pool[i] = clone
self._available[i] = clone
end
return self
end
function VFXPool:get(): Instance?
local obj = table.remove(self._available)
return obj
end
function VFXPool:release(obj: Instance)
-- Reset position, disable emitters
for _, emitter in obj:GetDescendants() do
if emitter:IsA("ParticleEmitter") then
emitter.Enabled = false
end
end
table.insert(self._available, obj)
endlocal VFXPool = {}
VFXPool.__index = VFXPool
function VFXPool.new(template: Instance, poolSize: number): typeof(setmetatable({}, VFXPool))
local self = setmetatable({}, VFXPool)
self._pool = table.create(poolSize)
self._available = table.create(poolSize)
for i = 1, poolSize do
local clone = template:Clone()
clone.Parent = workspace.VFXFolder
-- 禁用所有发射器
for _, emitter in clone:GetDescendants() do
if emitter:IsA("ParticleEmitter") then
emitter.Enabled = false
end
end
self._pool[i] = clone
self._available[i] = clone
end
return self
end
function VFXPool:get(): Instance?
local obj = table.remove(self._available)
return obj
end
function VFXPool:release(obj: Instance)
-- 重置位置,禁用发射器
for _, emitter in obj:GetDescendants() do
if emitter:IsA("ParticleEmitter") then
emitter.Enabled = false
end
end
table.insert(self._available, obj)
endMarkerReachedSignalPlaybackSpeedtask.delayCompletedMarkerReachedSignalPlaybackSpeedtask.delayCompleted-- BAD: unbounded particle creation with no cleanup
RunService.Heartbeat:Connect(function()
local emitter = Instance.new("ParticleEmitter")
emitter.Rate = 500 -- extremely high rate
emitter.Parent = somePart
-- never destroyed, accumulates forever
end)
-- GOOD: reuse a single emitter, burst when needed
local emitter = Instance.new("ParticleEmitter")
emitter.Rate = 0 -- manual emission only
emitter.Parent = somePart
local function burstParticles(count: number)
emitter:Emit(count)
end-- 错误:无限制创建粒子且不清理
RunService.Heartbeat:Connect(function()
local emitter = Instance.new("ParticleEmitter")
emitter.Rate = 500 -- 极高的发射率
emitter.Parent = somePart
-- 从未销毁,持续累积
end)
-- 正确:复用单个发射器,按需爆发
local emitter = Instance.new("ParticleEmitter")
emitter.Rate = 0 -- 仅手动发射
emitter.Parent = somePart
local function burstParticles(count: number)
emitter:Emit(count)
end-- BAD: 1024x1024 high-res texture for tiny particles
emitter.Texture = "rbxassetid://huge_4k_texture"
-- GOOD: 64x64 or 128x128 simple shape on transparent background
emitter.Texture = "rbxassetid://small_optimized_circle"
-- Use LightEmission = 1 with simple shapes for clean glow effects-- 错误:用1024x1024高分辨率纹理做微小粒子
emitter.Texture = "rbxassetid://huge_4k_texture"
-- 正确:用64x64或128x128透明背景的简单形状
emitter.Texture = "rbxassetid://small_optimized_circle"
-- 设置LightEmission = 1,配合简单形状实现干净的发光效果-- BAD: loading animations in a hot path synchronously
local function onAttack()
local anim = Instance.new("Animation")
anim.AnimationId = "rbxassetid://123456789"
local track = animator:LoadAnimation(anim) -- may yield on first load
track:Play()
end
-- GOOD: preload animations at character spawn
local attackAnim = Instance.new("Animation")
attackAnim.AnimationId = "rbxassetid://123456789"
local attackTrack: AnimationTrack -- forward declare
local function onCharacterAdded(character: Model)
local animator = character:WaitForChild("Humanoid"):WaitForChild("Animator")
attackTrack = animator:LoadAnimation(attackAnim)
attackTrack.Priority = Enum.AnimationPriority.Action
end
local function onAttack()
if attackTrack then
attackTrack:Play()
end
end-- 错误:在热点路径同步加载动画
local function onAttack()
local anim = Instance.new("Animation")
anim.AnimationId = "rbxassetid://123456789"
local track = animator:LoadAnimation(anim) -- 首次加载可能阻塞
track:Play()
end
-- 正确:在角色生成时预加载动画
local attackAnim = Instance.new("Animation")
attackAnim.AnimationId = "rbxassetid://123456789"
local attackTrack: AnimationTrack -- 提前声明
local function onCharacterAdded(character: Model)
local animator = character:WaitForChild("Humanoid"):WaitForChild("Animator")
attackTrack = animator:LoadAnimation(attackAnim)
attackTrack.Priority = Enum.AnimationPriority.Action
end
local function onAttack()
if attackTrack then
attackTrack:Play()
end
endCamera.CameraTypeScriptableAncestryChangedEndedShadows = falseCamera.CameraTypeScriptableAncestryChangedEndedShadows = false--[[
HitEffectSystem
Combines visual and audio feedback for combat hit registration.
Run on the CLIENT only (camera shake and local VFX).
]]
local TweenService = game:GetService("TweenService")
local RunService = game:GetService("RunService")
local HitEffectSystem = {}
-- Configuration
local DEFAULT_CONFIG = {
-- Flash
flashColor = Color3.new(1, 1, 1),
flashDuration = 0.15,
flashRevertDuration = 0.25,
-- Particles
particleBurstCount = 20,
particleLifetime = NumberRange.new(0.2, 0.5),
particleSpeed = NumberRange.new(8, 15),
particleSize = NumberSequence.new({
NumberSequenceKeypoint.new(0, 0.5),
NumberSequenceKeypoint.new(1, 0),
}),
particleColor = ColorSequence.new({
ColorSequenceKeypoint.new(0, Color3.new(1, 1, 1)),
ColorSequenceKeypoint.new(1, Color3.fromRGB(255, 200, 50)),
}),
particleTransparency = NumberSequence.new({
NumberSequenceKeypoint.new(0, 0),
NumberSequenceKeypoint.new(0.7, 0.3),
NumberSequenceKeypoint.new(1, 1),
}),
-- Sound
hitSoundId = "rbxassetid://123456789", -- replace with your asset
hitSoundVolume = 0.8,
hitSoundPitchVariation = 0.15, -- random pitch +/- this amount
-- Camera shake
shakeIntensity = 0.4,
shakeDuration = 0.2,
}
-- Pre-create a reusable particle emitter template
local function createHitEmitterTemplate(config: typeof(DEFAULT_CONFIG)): ParticleEmitter
local emitter = Instance.new("ParticleEmitter")
emitter.Name = "HitBurst"
emitter.Rate = 0 -- manual emission only
emitter.Lifetime = config.particleLifetime
emitter.Speed = config.particleSpeed
emitter.SpreadAngle = Vector2.new(180, 180) -- omnidirectional burst
emitter.Size = config.particleSize
emitter.Color = config.particleColor
emitter.Transparency = config.particleTransparency
emitter.LightEmission = 1
emitter.Drag = 5
emitter.RotSpeed = NumberRange.new(-180, 180)
return emitter
end
-- Emitter cache: one emitter per part (lazily created)
local emitterCache: { [BasePart]: ParticleEmitter } = {}
local emitterTemplate: ParticleEmitter? = nil
local function getOrCreateEmitter(part: BasePart, config: typeof(DEFAULT_CONFIG)): ParticleEmitter
if emitterCache[part] then
return emitterCache[part]
end
if not emitterTemplate then
emitterTemplate = createHitEmitterTemplate(config)
end
local emitter = emitterTemplate:Clone()
emitter.Parent = part
emitterCache[part] = emitter
-- Clean up if part is destroyed
part.Destroying:Connect(function()
emitterCache[part] = nil
end)
return emitter
end
--[[
Flash the target part white and tween back to original color.
]]
local function flashPart(part: BasePart, config: typeof(DEFAULT_CONFIG))
local originalColor = part.Color
part.Color = config.flashColor
local tweenBack = TweenService:Create(
part,
TweenInfo.new(config.flashRevertDuration, Enum.EasingStyle.Quad, Enum.EasingDirection.Out),
{ Color = originalColor }
)
tweenBack:Play()
end
--[[
Emit a burst of particles from the hit location.
]]
local function emitParticleBurst(part: BasePart, config: typeof(DEFAULT_CONFIG))
local emitter = getOrCreateEmitter(part, config)
emitter:Emit(config.particleBurstCount)
end
--[[
Play a one-shot hit sound with slight pitch variation.
]]
local function playHitSound(part: BasePart, config: typeof(DEFAULT_CONFIG))
local sound = Instance.new("Sound")
sound.SoundId = config.hitSoundId
sound.Volume = config.hitSoundVolume
sound.PlaybackSpeed = 1 + (math.random() - 0.5) * 2 * config.hitSoundPitchVariation
sound.RollOffMaxDistance = 80
sound.RollOffMinDistance = 5
sound.Parent = part
sound:Play()
sound.Ended:Connect(function()
sound:Destroy()
end)
end
--[[
Apply a short screen shake that decays over time.
]]
local function shakeCamera(config: typeof(DEFAULT_CONFIG))
local camera = workspace.CurrentCamera
if not camera then return end
local elapsed = 0
local intensity = config.shakeIntensity
local duration = config.shakeDuration
local connection: RBXScriptConnection
connection = RunService.RenderStepped:Connect(function(dt: number)
elapsed += dt
if elapsed >= duration then
connection:Disconnect()
return
end
local decay = 1 - (elapsed / duration)
local offsetX = (math.random() - 0.5) * 2 * intensity * decay
local offsetY = (math.random() - 0.5) * 2 * intensity * decay
camera.CFrame = camera.CFrame * CFrame.new(offsetX, offsetY, 0)
end)
end
--[[
Main entry point: trigger the full hit effect on a target part.
@param targetPart The BasePart that was hit (e.g., a character limb or NPC body).
@param overrides Optional table to override any DEFAULT_CONFIG values.
]]
function HitEffectSystem.play(targetPart: BasePart, overrides: { [string]: any }?)
-- Merge config with overrides
local config = table.clone(DEFAULT_CONFIG)
if overrides then
for key, value in overrides do
(config :: any)[key] = value
end
end
-- Fire all effects simultaneously
flashPart(targetPart, config)
emitParticleBurst(targetPart, config)
playHitSound(targetPart, config)
shakeCamera(config)
end
--[[
Clean up cached emitters (call when resetting scene or on player leave).
]]
function HitEffectSystem.cleanup()
for part, emitter in emitterCache do
emitter:Destroy()
end
table.clear(emitterCache)
end
return HitEffectSystem
--[[
USAGE EXAMPLE (in a LocalScript):
local HitEffectSystem = require(script.Parent.HitEffectSystem)
-- When a hit is confirmed (e.g., via RemoteEvent from server):
hitRemote.OnClientEvent:Connect(function(targetPart: BasePart)
HitEffectSystem.play(targetPart)
end)
-- Custom overrides for a critical hit:
hitRemote.OnClientEvent:Connect(function(targetPart: BasePart, isCritical: boolean)
if isCritical then
HitEffectSystem.play(targetPart, {
flashColor = Color3.fromRGB(255, 50, 50),
particleBurstCount = 40,
shakeIntensity = 0.8,
shakeDuration = 0.4,
hitSoundId = "rbxassetid://critical_hit_sound_id",
})
else
HitEffectSystem.play(targetPart)
end
end)
]]--[[
HitEffectSystem
结合视觉和音频反馈实现战斗击中判定。
仅在客户端运行(相机震动和本地VFX)。
]]
local TweenService = game:GetService("TweenService")
local RunService = game:GetService("RunService")
local HitEffectSystem = {}
-- 配置
local DEFAULT_CONFIG = {
-- 闪光
flashColor = Color3.new(1, 1, 1),
flashDuration = 0.15,
flashRevertDuration = 0.25,
-- 粒子
particleBurstCount = 20,
particleLifetime = NumberRange.new(0.2, 0.5),
particleSpeed = NumberRange.new(8, 15),
particleSize = NumberSequence.new({
NumberSequenceKeypoint.new(0, 0.5),
NumberSequenceKeypoint.new(1, 0),
}),
particleColor = ColorSequence.new({
ColorSequenceKeypoint.new(0, Color3.new(1, 1, 1)),
ColorSequenceKeypoint.new(1, Color3.fromRGB(255, 200, 50)),
}),
particleTransparency = NumberSequence.new({
NumberSequenceKeypoint.new(0, 0),
NumberSequenceKeypoint.new(0.7, 0.3),
NumberSequenceKeypoint.new(1, 1),
}),
-- 音效
hitSoundId = "rbxassetid://123456789", -- 替换为你的资源ID
hitSoundVolume = 0.8,
hitSoundPitchVariation = 0.15, -- 随机音高变化范围 +/- 该值
-- 相机震动
shakeIntensity = 0.4,
shakeDuration = 0.2,
}
-- 预创建可复用的粒子发射器模板
local function createHitEmitterTemplate(config: typeof(DEFAULT_CONFIG)): ParticleEmitter
local emitter = Instance.new("ParticleEmitter")
emitter.Name = "HitBurst"
emitter.Rate = 0 -- 仅手动发射
emitter.Lifetime = config.particleLifetime
emitter.Speed = config.particleSpeed
emitter.SpreadAngle = Vector2.new(180, 180) -- 全向爆发
emitter.Size = config.particleSize
emitter.Color = config.particleColor
emitter.Transparency = config.particleTransparency
emitter.LightEmission = 1
emitter.Drag = 5
emitter.RotSpeed = NumberRange.new(-180, 180)
return emitter
end
-- 发射器缓存:每个部件对应一个发射器(懒加载)
local emitterCache: { [BasePart]: ParticleEmitter } = {}
local emitterTemplate: ParticleEmitter? = nil
local function getOrCreateEmitter(part: BasePart, config: typeof(DEFAULT_CONFIG)): ParticleEmitter
if emitterCache[part] then
return emitterCache[part]
end
if not emitterTemplate then
emitterTemplate = createHitEmitterTemplate(config)
end
local emitter = emitterTemplate:Clone()
emitter.Parent = part
emitterCache[part] = emitter
-- 部件销毁时清理缓存
part.Destroying:Connect(function()
emitterCache[part] = nil
end)
return emitter
end
--[[
将目标部件闪白并补间恢复原颜色。
]]
local function flashPart(part: BasePart, config: typeof(DEFAULT_CONFIG))
local originalColor = part.Color
part.Color = config.flashColor
local tweenBack = TweenService:Create(
part,
TweenInfo.new(config.flashRevertDuration, Enum.EasingStyle.Quad, Enum.EasingDirection.Out),
{ Color = originalColor }
)
tweenBack:Play()
end
--[[
在击中位置发射粒子爆发。
]]
local function emitParticleBurst(part: BasePart, config: typeof(DEFAULT_CONFIG))
local emitter = getOrCreateEmitter(part, config)
emitter:Emit(config.particleBurstCount)
end
--[[
播放带轻微音高变化的单次击中音效。
]]
local function playHitSound(part: BasePart, config: typeof(DEFAULT_CONFIG))
local sound = Instance.new("Sound")
sound.SoundId = config.hitSoundId
sound.Volume = config.hitSoundVolume
sound.PlaybackSpeed = 1 + (math.random() - 0.5) * 2 * config.hitSoundPitchVariation
sound.RollOffMaxDistance = 80
sound.RollOffMinDistance = 5
sound.Parent = part
sound:Play()
sound.Ended:Connect(function()
sound:Destroy()
end)
end
--[[
应用随时间衰减的短时间屏幕震动。
]]
local function shakeCamera(config: typeof(DEFAULT_CONFIG))
local camera = workspace.CurrentCamera
if not camera then return end
local elapsed = 0
local intensity = config.shakeIntensity
local duration = config.shakeDuration
local connection: RBXScriptConnection
connection = RunService.RenderStepped:Connect(function(dt: number)
elapsed += dt
if elapsed >= duration then
connection:Disconnect()
return
end
local decay = 1 - (elapsed / duration)
local offsetX = (math.random() - 0.5) * 2 * intensity * decay
local offsetY = (math.random() - 0.5) * 2 * intensity * decay
camera.CFrame = camera.CFrame * CFrame.new(offsetX, offsetY, 0)
end)
end
--[[
主入口:在目标部件上触发完整的击中效果。
@param targetPart 被击中的BasePart(例如角色肢体或NPC身体)。
@param overrides 可选,覆盖DEFAULT_CONFIG的任意值。
]]
function HitEffectSystem.play(targetPart: BasePart, overrides: { [string]: any }?)
-- 合并配置与覆盖项
local config = table.clone(DEFAULT_CONFIG)
if overrides then
for key, value in overrides do
(config :: any)[key] = value
end
end
-- 同时触发所有效果
flashPart(targetPart, config)
emitParticleBurst(targetPart, config)
playHitSound(targetPart, config)
shakeCamera(config)
end
--[[
清理缓存的发射器(场景重置或玩家离开时调用)。
]]
function HitEffectSystem.cleanup()
for part, emitter in emitterCache do
emitter:Destroy()
end
table.clear(emitterCache)
end
return HitEffectSystem
--[[
使用示例(在LocalScript中):
local HitEffectSystem = require(script.Parent.HitEffectSystem)
-- 击中确认时(例如通过服务器发送的RemoteEvent):
hitRemote.OnClientEvent:Connect(function(targetPart: BasePart)
HitEffectSystem.play(targetPart)
end)
-- 暴击时自定义覆盖项:
hitRemote.OnClientEvent:Connect(function(targetPart: BasePart, isCritical: boolean)
if isCritical then
HitEffectSystem.play(targetPart, {
flashColor = Color3.fromRGB(255, 50, 50),
particleBurstCount = 40,
shakeIntensity = 0.8,
shakeDuration = 0.4,
hitSoundId = "rbxassetid://critical_hit_sound_id",
})
else
HitEffectSystem.play(targetPart)
end
end)
]]