【游戏讲坛】优雅解决LabelShadow组件在原生无效

分析LabelShadow组件无效的原因

LabelShadow组件在原生平台无效,原因是LabelShadow组件只在Web平台上实现了该功能,在原生平台上没有实现该功能。

简单分析一下源码(文件:engine/cocos2d/core/render/utils/label/ttf.js)。游戏引擎是如何使用LabelShadow组件进行描边的。

通关观察ttf.js源码中 _updateTexture 和 _setupShadow两个函数。分析出描边所需要的四个参数:

  • shadowColor: 描边的样式(RGBA)
  • shadowBlur: 描边模糊的宽度
  • shadowOffsetX: 描边的X轴的偏移量
  • shadowOffsetY:描边的Y轴的偏移量
_updateTexture() {
    ......
    ......
    // only one set shadow and outline
    if (_shadowComp) {
        this._setupShadow();
    }
    ......
    ......
}

_setupShadow () {
    _context.shadowColor = `rgba(${_shadowColor.r}, ${_shadowColor.g}, ${_shadowColor.b}, ${_shadowColor.a / 255})`;
    _context.shadowBlur = _shadowComp.blur;
    _context.shadowOffsetX = _shadowComp.offset.x;
    _context.shadowOffsetY = -_shadowComp.offset.y;
}

如何解决LabelShadow组件在原生平台(Android)无效

1. 修改C++层相关代码

1.找到 CanvasRenderingContext2D类并进行简单的修改(文件目录在 cocos/platform/CCCanvasRenderingContext2D.h)。添加上面分析出来的变量。

//
class CC_DLL CanvasRenderingContext2D {
    // TODO 虣虣 阴影 begin
    void set_shadowColor(const std::string& shadowColor);
    void set_shadowBlur(float shadowBlur);
    void set_shadowOffsetX(float shadowOffsetX);
    void set_shadowOffsetY(float shadowOffsetY);
    // TODO 虣虣 阴影 end
    
    
    // TODO 虣虣 阴影 begin
    std::string  _shadowColor = "#000";
    float _shadowBlur = 1.0f;
    float _shadowOffsetX = 1.0f;
    float _shadowOffsetY = 1.0f;
    // TODO 虣虣 阴影 end
}

2.在相关android平台下修改这些变量的读写属性,以及调用Java层代码,文件所在目录(cocos/platform/android/CCCanvasRenderingContext2D-android.cpp)


// TODO 虣虣 阴影 begin
void setShadowColor(float r, float g, float b, float a)
{
    JniHelper::callObjectVoidMethod(_obj, JCLS_CANVASIMPL, "setShadowColor", r, g, b, a);
}
void setShadowBlur(float shadowBlur)
{
    JniHelper::callObjectVoidMethod(_obj, JCLS_CANVASIMPL, "setShadowBlur", shadowBlur);
}
void setShadowOffsetX(float shadowOffsetX)
{
    JniHelper::callObjectVoidMethod(_obj, JCLS_CANVASIMPL, "setShadowOffsetX", shadowOffsetX);
}
void setShadowOffsetY(float shadowOffsetY)
{
    JniHelper::callObjectVoidMethod(_obj, JCLS_CANVASIMPL, "setShadowOffsetY", shadowOffsetY);
}
// TODO 虣虣 阴影 end



// TODO 虣虣 阴影 begin
void CanvasRenderingContext2D::set_shadowColor(const std::string& shadowColor) {
    CSSColorParser::Color color = CSSColorParser::parse(shadowColor);
    _impl->setShadowColor(color.r/255.0f, color.g/255.0f, color.b/255.0f, color.a);
}

void CanvasRenderingContext2D::set_shadowBlur(float shadowBlur) {
    _impl->setShadowBlur(shadowBlur);
}

void CanvasRenderingContext2D::set_shadowOffsetX(float shadowOffsetX) {
    _impl->setShadowOffsetX(shadowOffsetX);
}

void CanvasRenderingContext2D::set_shadowOffsetY(float shadowOffsetY) {
    _impl->setShadowOffsetX(shadowOffsetY);
}
// TODO 虣虣 阴影 end

2. 修改JAVA层相关代码

修改 cocos/platform/android/java/src/org/cocos2dx/lib/CanvasRenderingContext2DImpl.java 文件,修改如下

// cocos/platform/android/java/src/org/cocos2dx/lib/CanvasRenderingContext2DImpl.java
public class CanvasRenderingContext2DImpl {
    // TODO 虣虣 阴影 begin
    private int mShadowStyleR = 0;
    private int mShadowStyleG = 0;
    private int mShadowStyleB = 0;
    private int mShadowStyleA = 0;
    private float mShadowBlur = 0;
    private float mShadowOffsetX = 0;
    private float mShadowOffsetY = 0;
    // TODO 虣虣 阴影 end
    
    
    
    // TODO 虣虣 阴影 begin
    // 设置阴影颜色
    private void setShadowColor(float r, float g, float b, float a) {
        // Log.d(TAG, "setShadowStyle: " + r + ", " + g + ", " + b + ", " + a);
        mShadowStyleR = (int)(r * 255.0f);
        mShadowStyleG = (int)(g * 255.0f);
        mShadowStyleB = (int)(b * 255.0f);
        mShadowStyleA = (int)(a * 255.0f);
    }
    // 设置描边宽度
    private void setShadowBlur(float shadowBlur) {
        mShadowBlur = shadowBlur;
    }
    // 设置描边的偏移X
    private void setShadowOffsetX(float shadowOffsetX) {
        mShadowOffsetX = shadowOffsetX;
    }
    // 设置描边的偏移Y
    private void setShadowOffsetY(float shadowOffsetY) {
        mShadowOffsetY = shadowOffsetY;
    }
    // TODO 虣虣 阴影 end
    
    
    
    
    private void fillText(String text, float x, float y, float maxWidth) {
//        Log.d(TAG, "this: " + this + ", fillText: " + text + ", " + x + ", " + y + ", " + ", " + maxWidth);
        createTextPaintIfNeeded();
        mTextPaint.setARGB(mFillStyleA, mFillStyleR, mFillStyleG, mFillStyleB);
        mTextPaint.setStyle(Paint.Style.FILL);

        // TODO 虣虣 阴影 begin
        int color = Color.argb(mShadowStyleA, mShadowStyleR, mShadowStyleG, mShadowStyleB);
        mTextPaint.setShadowLayer(mShadowBlur, mShadowOffsetX, mShadowOffsetX, color);
        // TODO 虣虣 阴影 end

        scaleX(mTextPaint, text, maxWidth);
        Point pt = convertDrawPoint(new Point(x, y), text);
        mCanvas.drawText(text, pt.x, pt.y, mTextPaint);
    }
    
}

3. 注册相关属性给JS层调用

此处为了方便些文章,我是手动写到文件里面的(相关文件 cocos/scripting/jsb-bindings/manual/jsb-cocos2dx-manual.cpp),这里可以使用自动绑定相关功能。

// TODO 虣虣 阴影 begin
BIND_PROP_WITH_TYPE__CONV_FUNC__RETURN(CanvasRenderingContext2D, shadowColor, std::string, seval_to_std_string, setString)
BIND_PROP_WITH_TYPE__CONV_FUNC__RETURN(CanvasRenderingContext2D, shadowBlur, float, seval_to_float, setFloat)
BIND_PROP_WITH_TYPE__CONV_FUNC__RETURN(CanvasRenderingContext2D, shadowOffsetX, float, seval_to_float, setFloat)
BIND_PROP_WITH_TYPE__CONV_FUNC__RETURN(CanvasRenderingContext2D, shadowOffsetY, float, seval_to_float, setFloat)
// TODO 虣虣 阴影 end


// TODO 虣虣 阴影 begin
_SE_DEFINE_PROP(CanvasRenderingContext2D, shadowColor)
_SE_DEFINE_PROP(CanvasRenderingContext2D, shadowBlur)
_SE_DEFINE_PROP(CanvasRenderingContext2D, shadowOffsetX)
_SE_DEFINE_PROP(CanvasRenderingContext2D, shadowOffsetY)
// TODO 虣虣 阴影 end

如何解决LabelShadow组件在原生平台(IOS)无效

前面已经修改过C++部分的代码,这里不再过多的叙说,简单的说一下IOS需要修改的内容。IOS需要修改的文件在 cocos/platform/apple/CCCanvasRenderingContext2D-apple.mm文件中。

  • 设置相关字体阴影属性
// CCCanvasRenderingContext2D-apple.mm
@interface CanvasRenderingContext2DImpl : NSObject {
....
    // TODO 虣虣 begin 文字阴影
    // 阴影颜色
    cocos2d::Color4F _shadowStyle;
    // 阴影模糊的宽度
    float _shadowBlur;
    // 阴影X偏移量
    float _shadowOffsetX;
    // 阴影Y偏移量
    float _shadowOffsetY;
    // TODO 虣虣 end
....
}
....
@property (nonatomic, assign) float shadowBlur;
@property (nonatomic, assign) float shadowOffsetX;
@property (nonatomic, assign) float shadowOffsetY;


@synthesize shadowBlur = _shadowBlur;
@synthesize shadowOffsetX = _shadowOffsetX;
@synthesize shadowOffsetY = _shadowOffsetY;

....

  • 设置相关字体阴影的属性,共JS层调用
// CCCanvasRenderingContext2D-apple.mm
// TODO 虣虣 begin 设置相关属性,共JS调用。
// 设置字体阴影相关属性(颜色,粗细,偏移)

-(void) setShadowStyleWithRed:(CGFloat) r green:(CGFloat) g blue:(CGFloat) b
    alpha:(CGFloat) a {
    _shadowStyle.r = r;
    _shadowStyle.g = g;
    _shadowStyle.b = b;
    _shadowStyle.a = a;
}

void CanvasRenderingContext2D::set_shadowColor(const std::string &shadowColor) {
    CSSColorParser::Color color = CSSColorParser::parse(shadowColor);
    [_impl setShadowStyleWithRed:color.r/255.0f green:color.g/255.0f blue:color.b/255.0f alpha:color.a];
}

void CanvasRenderingContext2D::set_shadowBlur(float shadowBlur) {
    _shadowBlur = shadowBlur;
    _impl.shadowBlur = _shadowBlur;
}

void CanvasRenderingContext2D::set_shadowOffsetX(float shadowOffsetX) {
    _shadowOffsetX = shadowOffsetX;
    _impl.shadowOffsetX = shadowOffsetX;
}

void CanvasRenderingContext2D::set_shadowOffsetY(float shadowOffsetY) {
    _shadowOffsetY = shadowOffsetY;
    _impl.shadowOffsetY = _shadowOffsetY;
}
// TODO 虣虣 end
  • 在fillText函数增加相关代码
-(void) fillText:(NSString*) text x:(CGFloat) x y:(CGFloat) y maxWidth:(CGFloat) maxWidth {
    ....
    
    // text color
    CGContextSetRGBFillColor(_context, _fillStyle.r, _fillStyle.g, _fillStyle.b, _fillStyle.a);
    CGContextSetShouldSubpixelQuantizeFonts(_context, false);
    CGContextBeginTransparencyLayerWithRect(_context, CGRectMake(0, 0, _width, _height), nullptr);
    CGContextSetTextDrawingMode(_context, kCGTextFill);

    // TODO 虣虣 begin
    NSColor* color = [NSColor colorWithRed:_shadowStyle.r green:_shadowStyle.g blue:_shadowStyle.b alpha:_shadowStyle.a];
    CGContextSetShadowWithColor(_context, CGSizeMake(_shadowOffsetX, _shadowOffsetY), _shadowBlur, color.CGColor);
    // TODO 虣虣 end

    ....

    CGContextEndTransparencyLayer(_context);

    [self restoreContext];
}

说明: Android平台已经测试过,IOS平台相关代码没有经过测试
如果你对所分享的内容感兴趣,可以关注微信公众号【游戏讲坛】。
游戏讲坛已开通微信公众号交流群。如想加入请关注微信公众号。
87e21535478881

15赞

感谢分享,大佬牛皮

2.4.6上实测,ios上会翻转
更改,_shadowOffsetY前加负号

完整:

    // TODO 虣虣 begin
    NSColor* color = [NSColor colorWithRed:_shadowStyle.r green:_shadowStyle.g blue:_shadowStyle.b alpha:_shadowStyle.a];
    CGContextSetShadowWithColor(_context, CGSizeMake(_shadowOffsetX, -_shadowOffsetY), _shadowBlur, color.CGColor);
    // TODO 虣虣 end

感谢,IOS平台没有测过

非常感谢楼主大佬提供文字Shadow在原生平台的解决方案,Android测试没问题!
有2点需注意:
1.若Blur值设为0,Shadow会无效,可以设成0.0001
2.下面这行代码大佬可能手滑了,写了两个mShadowOffsetX, mShadowOffsetX,左边改成mShadowOffsetY

1赞

在2.5版本上修改的android平台,没搞定
能提供下修改后的4个文件吗?

有2.5版本么

2.4
手滑

为什么这么简单的修改,引擎组没有合进去呢 :rofl: :rofl: :rofl:

请问,要解决windows上的描边该怎么办呢 :joy:

发现文本有换行和没有换行的效果不一样,左边是没换行的 :joy:如下图

还有在IOS上文本有换行上没有shadow效果,如下图
image

最后shadow效果只对ttf字体生效,系统字没有效果

mark一下

image
下面的要改成setShadowOffsetY喔

1赞