Looking for a fix/workaround for blurry decal textures on texture or transparency change

  1. Issue
    PWNed BY 14_00 [PROTOTYPE _ J) - Roblox Studio 2025-02-16 13-53-49.mp4 - Google Drive
    The face’s decal becomes blurry on change. I would like for it to already be high-quality when changing it for my game

  2. What solutions have you tried so far? Did you look for solutions on the Developer Hub?
    I tried preloading the face’s texture each time I change the texture. And preloading it prematurely, before the face changes.
    I tried layering decals on the face with a transparency of 0.999 and then making them visible. They also get briefly blurry on transparency changes as well.
    I also messed around with Workspace.ModelStreamingBehavior. I think that’s completely unrelated though lol

If it helps, here’s my current code (preloadAsync runs on an UnreliableRemoteEvent—I can assure it runs before the face change by the console output):

local character = script.Parent.Parent.Parent
local dir = script.Parent
local preloadAsync = game.ReplicatedStorage:WaitForChild("RemoteEvents"):WaitForChild("PreloadAsync")

local face = character:WaitForChild("Head"):FindFirstChildOfClass("Decal")

local lowHpFace = dir.LowHp
local damagedFace = dir.Damaged
local normalFaceTexture = face.Texture

local LOW_HP_RATIO = 4 -- 100 maxhealth would be 25 health

local humanoid = character:WaitForChild("Humanoid")

local previousHp = humanoid.Health
local damagedFaceThread = nil
local DAMAGE_EXPRESSION_DURATION = 1

-- preload faces on script run
preloadAsync:FireAllClients({lowHpFace.Texture, damagedFace.Texture})

-- meant to run on task.spawn when not forever
local function onDamaged(forever : boolean)
	preloadAsync:FireAllClients({damagedFace.Texture})
	face.Texture = damagedFace.Texture

	if not forever then
		preloadAsync:FireAllClients({normalFaceTexture})
		task.wait(DAMAGE_EXPRESSION_DURATION)

		face.Texture = normalFaceTexture
		damagedFaceThread = nil
	end
end

local function cancelDamageThread()
	if damagedFaceThread then
		task.cancel(damagedFaceThread)
		damagedFaceThread = nil
	end
end

humanoid:GetPropertyChangedSignal("Health"):Connect(function()
	local isLowHp = humanoid.Health <= humanoid.MaxHealth / LOW_HP_RATIO

	if isLowHp then
		if not damagedFaceThread then
			damagedFaceThread = task.spawn(onDamaged)
		
			repeat
				task.wait()
			until not damagedFaceThread
				
			if not damagedFaceThread then	
				face.Texture = lowHpFace.Texture
			end
			
			previousHp = humanoid.Health
			return
		end
	else
		-- determine if hp was gained
		if humanoid.Health > previousHp and not isLowHp then
			cancelDamageThread()
			face.Texture = normalFaceTexture
		else
			-- on hit: avoid overlapping
			if not damagedFaceThread then
				damagedFaceThread = task.spawn(onDamaged)
			end
		end
	end

	previousHp = humanoid.Health
end)

humanoid.Died:Connect(function()
	cancelDamageThread()
	
	onDamaged(true)
end)

Any help’s appreciated. I at least know it’s possible due to Forsaken doing it smoothly—I just don’t know how.

Note to everyone having the same issue because I found similar posts, but no concrete fixes.

I found a solution. This is only an issue with decals specifically. To be frank—I don’t know if I should report this as an engine issue to Roblox, since they added that feature intentionally (I’m assuming). I guess not. So I’m just like “whatever, I found a workaround”. At least a toggle or documentation would be nice.

I noticed how character faces in Forsaken have a little unusual depth to them from certain angles. And then thought “oh, wait, aren’t those SurfaceGuis?”. Then also remembered how I never had this issue working with UI. And yep, they were.

Sure enough, just weld a face part to the Head’s FaceFrontAttachment. Like so (aditionally, make sure the FacePart is unanchored):

And just change the SurfaceGui’s ImageLabel’s Image property in your script. You should preload the changed image because the image is temporarily empty as it loads. You should probably do so at script setup.

Looks off at certain angles but it’s pretty much unnoticeable and tolerable for such a fix, specially if your character’s head follows the camera.

Did not find any similar posts, so I think I should mention this:
If someone does not want to use surfaceguis for faces, you can keep the decals of faces you want on the head, and toggle their LocalTransparencyModifier instead. This should prevent it from reloading all of the avatar’s textures.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.