Cocos Creator 2.4.15 原生平台Label描边与阴影功能修复指南

概述

本文档提供了针对 Cocos Creator 2.4.15 中,Label 组件的描边(Outline)和阴影(Shadow)在原生平台(Android/iOS)无效问题的完整修复方案。引擎的这两个功能在 Web 平台基于 Canvas 实现,但在原生平台缺少对应的底层实现,需要通过修改引擎 C++ 及平台特定代码来支持。

功能对比与问题根因

功能Web 平台原生平台 (修复前)
描边 (Outline)通过 CanvasRenderingContext2D.lineWidth 和多次偏移绘制实现底层 CanvasRenderingContext2D 类未实现 lineWidth 等相关属性设置
阴影 (Shadow)通过 CanvasRenderingContext2D.shadowBlur 等属性实现底层 CanvasRenderingContext2D 类未实现 shadowBlur 等相关属性设置

修改总览

修复的核心是在引擎的原生 CanvasRenderingContext2D 类及其各平台实现中,添加描边和阴影所需的属性和方法。

需要修改的文件清单

  1. C++ 公共头文件与绑定

    • cocos/platform/CCCanvasRenderingContext2D.h (新增属性与方法声明)
    • cocos/scripting/jsb-bindings/manual/jsb-cocos2dx-manual.cpp (可选,用于JS绑定)
  2. Android 平台

    • cocos/platform/android/CCCanvasRenderingContext2D-android.cpp (C++实现与JNI桥接)
    • cocos/platform/android/java/src/org/cocos2dx/lib/CanvasRenderingContext2DImpl.java (Java层具体实现)
  3. iOS 平台

    • cocos/platform/apple/CCCanvasRenderingContext2D-apple.mm (iOS/macOS 具体实现)

详细修改步骤

第一步:修改 C++ 公共头文件

文件: cocos/platform/CCCanvasRenderingContext2D.h

CanvasRenderingContext2D 类中添加描边和阴影所需的成员变量与公共方法。

class CC_DLL CanvasRenderingContext2D {
public:
    // ... 现有代码 ...

    // ========== 以下是需要添加的描边相关接口 ==========
    void set_lineWidth(float lineWidth);
    void set_lineJoin(const std::string& lineJoin);
    void set_lineCap(const std::string& lineCap);
    void set_strokeStyle(const std::string& strokeStyle); // 描边颜色

    // ========== 以下是需要添加的阴影相关接口 ==========
    void set_shadowColor(const std::string& shadowColor);
    void set_shadowBlur(float shadowBlur);
    void set_shadowOffsetX(float shadowOffsetX);
    void set_shadowOffsetY(float shadowOffsetY);

private:
    // ... 现有成员变量 ...

    // ========== 以下是需要添加的私有成员变量 ==========
    // 描边属性
    float _lineWidth = 1.0f;
    std::string _lineJoin = "miter";
    std::string _lineCap = "butt";
    std::string _strokeStyle = "#000";

    // 阴影属性
    std::string _shadowColor = "#000";
    float _shadowBlur = 0.0f;
    float _shadowOffsetX = 0.0f;
    float _shadowOffsetY = 0.0f;
};

第二步:修改 Android 平台 C++ 实现与 JNI

文件: cocos/platform/android/CCCanvasRenderingContext2D-android.cpp

  1. CanvasRenderingContext2D 的实现部分添加方法定义,将调用转发给 JNI 对象 _impl

    // 在文件中的 CanvasRenderingContext2D 方法实现区域添加
    // 描边方法实现
    void CanvasRenderingContext2D::set_lineWidth(float lineWidth) {
        _impl->setLineWidth(lineWidth);
    }
    void CanvasRenderingContext2D::set_lineJoin(const std::string& lineJoin) {
        _impl->setLineJoin(lineJoin);
    }
    void CanvasRenderingContext2D::set_lineCap(const std::string& lineCap) {
        _impl->setLineCap(lineCap);
    }
    void CanvasRenderingContext2D::set_strokeStyle(const std::string& strokeStyle) {
        CSSColorParser::Color color = CSSColorParser::parse(strokeStyle);
        _impl->setStrokeStyle(color.r/255.0f, color.g/255.0f, color.b/255.0f, color.a);
    }
    
    // 阴影方法实现
    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->setShadowOffsetY(shadowOffsetY);
    }
  2. 在 JNI 桥接类 CanvasRenderingContext2DImpl 中添加对应的 JNI 方法声明与调用

    // 在文件中的 CanvasRenderingContext2DImpl 类中添加
    // 描边 JNI 方法
    void setLineWidth(float lineWidth) {
        JniHelper::callObjectVoidMethod(_obj, JCLS_CANVASIMPL, "setLineWidth", lineWidth);
    }
    void setLineJoin(const std::string& lineJoin) {
        JniHelper::callObjectVoidMethod(_obj, JCLS_CANVASIMPL, "setLineJoin", lineJoin);
    }
    void setLineCap(const std::string& lineCap) {
        JniHelper::callObjectVoidMethod(_obj, JCLS_CANVASIMPL, "setLineCap", lineCap);
    }
    void setStrokeStyle(float r, float g, float b, float a) {
        JniHelper::callObjectVoidMethod(_obj, JCLS_CANVASIMPL, "setStrokeStyle", r, g, b, a);
    }
    
    // 阴影 JNI 方法
    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);
    }

第三步:修改 Android Java 实现层

文件: cocos/platform/android/java/src/org/cocos2dx/lib/CanvasRenderingContext2DImpl.java

在 Java 类中实现具体的描边和阴影逻辑。

  1. 添加私有成员变量

    public class CanvasRenderingContext2DImpl {
        // ... 现有变量 ...
    
        // ========== 描边属性 ==========
        private float mLineWidth = 1.0f;
        private String mLineJoin = "miter";
        private String mLineCap = "butt";
        private int mStrokeStyleR = 0;
        private int mStrokeStyleG = 0;
        private int mStrokeStyleB = 0;
        private int mStrokeStyleA = 255;
    
        // ========== 阴影属性 ==========
        private int mShadowColorR = 0;
        private int mShadowColorG = 0;
        private int mShadowColorB = 0;
        private int mShadowColorA = 0;
        private float mShadowBlur = 0.0f;
        private float mShadowOffsetX = 0.0f;
        private float mShadowOffsetY = 0.0f;
    }
  2. 添加设置属性的私有方法

    // 描边设置方法
    private void setLineWidth(float lineWidth) { mLineWidth = lineWidth; }
    private void setLineJoin(String lineJoin) { mLineJoin = lineJoin; }
    private void setLineCap(String lineCap) { mLineCap = lineCap; }
    private void setStrokeStyle(float r, float g, float b, float a) {
        mStrokeStyleR = (int)(r * 255);
        mStrokeStyleG = (int)(g * 255);
        mStrokeStyleB = (int)(b * 255);
        mStrokeStyleA = (int)(a * 255);
    }
    
    // 阴影设置方法
    private void setShadowColor(float r, float g, float b, float a) {
        mShadowColorR = (int)(r * 255);
        mShadowColorG = (int)(g * 255);
        mShadowColorB = (int)(b * 255);
        mShadowColorA = (int)(a * 255);
    }
    private void setShadowBlur(float shadowBlur) { mShadowBlur = shadowBlur; }
    private void setShadowOffsetX(float shadowOffsetX) { mShadowOffsetX = shadowOffsetX; }
    private void setShadowOffsetY(float shadowOffsetY) { mShadowOffsetY = shadowOffsetY; }
  3. 在绘制文本的方法中应用属性
    找到 fillTextdrawText 等方法(具体名称可能略有不同),在设置 Paint 对象时应用描边和阴影属性。

    对于描边:在绘制文本前,需要配置一个用于描边的 Paint 对象 (strokePaint)。

    // 示例逻辑
    if (mLineWidth > 0) {
        Paint strokePaint = new Paint(paint); // 复制填充画笔的字体等配置
        strokePaint.setStyle(Paint.Style.STROKE);
        strokePaint.setStrokeWidth(mLineWidth);
        strokePaint.setColor(Color.argb(mStrokeStyleA, mStrokeStyleR, mStrokeStyleG, mStrokeStyleB));
        // 使用 strokePaint 绘制文本,实现描边效果
        // 注意:引擎原生的描边效果可能是通过多次偏移绘制实现的,此处需模拟该逻辑。
    }

    对于阴影:在 Android 中,可以通过 Paint.setShadowLayer 来简单实现文本阴影。

    // 在绘制前设置阴影
    if (mShadowBlur > 0 && mShadowColorA > 0) {
        paint.setShadowLayer(mShadowBlur, mShadowOffsetX, mShadowOffsetY,
                Color.argb(mShadowColorA, mShadowColorR, mShadowColorG, mShadowColorB));
    } else {
        paint.clearShadowLayer();
    }

第四步:修改 iOS 平台实现

文件: cocos/platform/apple/CCCanvasRenderingContext2D-apple.mm

iOS 端的修改与 Android 端类似,但使用 Core Graphics API。

  1. 在实现类中添加对应的 Objective-C 方法

    // 描边方法
    void CanvasRenderingContext2D::set_lineWidth(float lineWidth) {
        // 保存线宽,在绘制时使用
        _lineWidth = lineWidth;
    }
    void CanvasRenderingContext2D::set_strokeStyle(const std::string& strokeStyle) {
        // 解析并保存描边颜色
        CSSColorParser::Color color = CSSColorParser::parse(strokeStyle);
        _strokeStyleR = color.r / 255.0f;
        _strokeStyleG = color.g / 255.0f;
        _strokeStyleB = color.b / 255.0f;
        _strokeStyleA = color.a;
    }
    // ... 其他 set_lineJoin, set_lineCap 类似
    
    // 阴影方法
    void CanvasRenderingContext2D::set_shadowColor(const std::string& shadowColor) {
        CSSColorParser::Color color = CSSColorParser::parse(shadowColor);
        _shadowColorR = color.r / 255.0f;
        _shadowColorG = color.g / 255.0f;
        _shadowColorB = color.b / 255.0f;
        _shadowColorA = color.a;
    }
    void CanvasRenderingContext2D::set_shadowBlur(float shadowBlur) {
        _shadowBlur = shadowBlur;
    }
    void CanvasRenderingContext2D::set_shadowOffsetX(float shadowOffsetX) {
        _shadowOffsetX = shadowOffsetX;
    }
    void CanvasRenderingContext2D::set_shadowOffsetY(float shadowOffsetY) {
        _shadowOffsetY = shadowOffsetY;
    }
  2. 在绘制函数中应用属性
    找到 drawText 或相关的绘制函数。在 Core Graphics 上下文 (CGContextRef) 中设置属性。

    对于描边

    CGContextSetLineWidth(context, _lineWidth);
    CGContextSetLineJoin(context, [self convertLineJoin:_lineJoin]); // 需要转换字符串为 CGLineJoin
    CGContextSetLineCap(context, [self convertLineCap:_lineCap]); // 需要转换字符串为 CGLineCap
    CGContextSetRGBStrokeColor(context, _strokeStyleR, _strokeStyleG, _strokeStyleB, _strokeStyleA);
    // 使用 CGContextSetTextDrawingMode 和多次绘制来实现描边效果

    对于阴影

    CGColorRef shadowColor = CGColorCreateCreateGenericRGB(_shadowColorR, _shadowColorG, _shadowColorB, _shadowColorA);
    CGSize offset = CGSizeMake(_shadowOffsetX, _shadowOffsetY);
    CGContextSetShadowWithColor(context, offset, _shadowBlur, shadowColor);
    CGColorRelease(shadowColor);

验证测试

  1. 编译:完成修改后,重新编译 Android 和 iOS 工程。
  2. 测试场景

    • 在场景中创建一个 Label 节点。
    • 添加 LabelOutline 组件,设置描边颜色和宽度。
    • 添加 LabelShadow 组件,设置阴影颜色、模糊度和偏移。
    • 打包到原生平台运行,检查效果是否与 Web 平台一致。

注意事项

  1. 性能:描边(特别是较宽描边)和阴影会显著增加绘制开销,在频繁更新或大量文本时需注意性能。
  2. 平台差异:不同平台(Android/iOS)的图形 API 对阴影和描边的实现细节可能有细微差异,最终效果需以实际运行效果为准。
  3. 引擎版本:本指南针对 Cocos Creator 2.4.15 及对应的 Cocos2d-x 引擎版本。其他版本的文件路径和代码结构可能不同。
  4. JSB 绑定:如果修改后 JavaScript 调用无效,可能需要检查或更新 jsb-cocos2dx-manual.cpp 中的 JSB 绑定代码,确保新增的 C++ 方法被正确暴露给 JavaScript 层。

标签: none

添加新评论