Files
mp-xieyingeng/assets/scripts/utils/roundedMaterial.utils.ts
2026-05-03 22:28:22 +08:00

100 lines
2.5 KiB
TypeScript
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.
import { EffectAsset, Material, Sprite, Vec4 } from 'cc';
/**
* 圆角 Sprite 材质工具。
*
* 基于 rounded-sprite.effect 的 SDF alpha 裁剪实现,直接作用于 Sprite 的自定义材质。
* 每个 Sprite 都会获得独立 Material避免 roundedParams / uvRect 互相覆盖。
*/
let cachedTemplate: Material | null = null;
let cachedEffectRef: EffectAsset | null = null;
const getOrCreateTemplate = (effectAsset: EffectAsset): Material => {
if (cachedTemplate && cachedEffectRef === effectAsset) {
return cachedTemplate;
}
const template = new Material();
template.initialize({
effectAsset,
defines: { USE_TEXTURE: true },
});
cachedTemplate = template;
cachedEffectRef = effectAsset;
return template;
};
const extractUvRect = (sprite: Sprite): Vec4 => {
const spriteFrame = sprite.spriteFrame;
if (!spriteFrame) {
return new Vec4(0, 0, 1, 1);
}
const uv = spriteFrame.uv;
if (!uv || uv.length < 8) {
return new Vec4(0, 0, 1, 1);
}
const u0 = uv[0];
const v0 = uv[1];
const u1 = uv[2];
const v1 = uv[3];
const u2 = uv[4];
const v2 = uv[5];
const u3 = uv[6];
const v3 = uv[7];
const minU = Math.min(u0, u1, u2, u3);
const maxU = Math.max(u0, u1, u2, u3);
const minV = Math.min(v0, v1, v2, v3);
const maxV = Math.max(v0, v1, v2, v3);
const rangeU = maxU - minU;
const rangeV = maxV - minV;
if (rangeU <= 0 || rangeV <= 0) {
return new Vec4(0, 0, 1, 1);
}
return new Vec4(minU, minV, rangeU, rangeV);
};
export const applyRoundedCorner = (
sprite: Sprite,
effectAsset: EffectAsset,
width: number,
height: number,
cornerRadius = 0.1,
grayscale = false,
): void => {
if (!sprite || !sprite.isValid) {
console.warn('[roundedMaterial] Invalid sprite, skipping');
return;
}
if (!effectAsset) {
console.warn('[roundedMaterial] EffectAsset is null, skipping');
return;
}
const template = getOrCreateTemplate(effectAsset);
const materialInstance = new Material();
const defines: Record<string, boolean> = { USE_TEXTURE: true };
if (grayscale) {
defines.IS_GRAY = true;
}
materialInstance.initialize({
effectAsset,
defines,
});
const params = new Vec4(cornerRadius, 0, width, height);
materialInstance.setProperty('roundedParams', params);
materialInstance.setProperty('uvRect', extractUvRect(sprite));
sprite.customMaterial = materialInstance;
};