Cocos Creator 2.4.15 原生平台Label描边与阴影功能修复指南
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 类及其各平台实现中,添加描边和阴影所需的属性和方法。
需要修改的文件清单
C++ 公共头文件与绑定
cocos/platform/CCCanvasRenderingContext2D.h(新增属性与方法声明)cocos/scripting/jsb-bindings/manual/jsb-cocos2dx-manual.cpp(可选,用于JS绑定)
Android 平台
cocos/platform/android/CCCanvasRenderingContext2D-android.cpp(C++实现与JNI桥接)cocos/platform/android/java/src/org/cocos2dx/lib/CanvasRenderingContext2DImpl.java(Java层具体实现)
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
在
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); }在 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 类中实现具体的描边和阴影逻辑。
添加私有成员变量
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; }添加设置属性的私有方法
// 描边设置方法 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; }在绘制文本的方法中应用属性
找到fillText或drawText等方法(具体名称可能略有不同),在设置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。
在实现类中添加对应的 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; }在绘制函数中应用属性
找到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);
验证测试
- 编译:完成修改后,重新编译 Android 和 iOS 工程。
测试场景:
- 在场景中创建一个 Label 节点。
- 添加
LabelOutline组件,设置描边颜色和宽度。 - 添加
LabelShadow组件,设置阴影颜色、模糊度和偏移。 - 打包到原生平台运行,检查效果是否与 Web 平台一致。
注意事项
- 性能:描边(特别是较宽描边)和阴影会显著增加绘制开销,在频繁更新或大量文本时需注意性能。
- 平台差异:不同平台(Android/iOS)的图形 API 对阴影和描边的实现细节可能有细微差异,最终效果需以实际运行效果为准。
- 引擎版本:本指南针对 Cocos Creator 2.4.15 及对应的 Cocos2d-x 引擎版本。其他版本的文件路径和代码结构可能不同。
- JSB 绑定:如果修改后 JavaScript 调用无效,可能需要检查或更新
jsb-cocos2dx-manual.cpp中的 JSB 绑定代码,确保新增的 C++ 方法被正确暴露给 JavaScript 层。