如何在SceneKit中使UIBezierPath的lineWidth属性生效?
问题:SceneKit中UIBezierPath的lineWidth设置无效,无法生成粗3D线条
在SceneKit中使用UIBezierPath构建SCNShape时,设置路径的lineWidth属性完全无效,生成的线条极细,达不到预期的粗线效果。相关代码示例如下:
let hugePath = UIBezierPath() hugePath.lineWidth = 40.0 // 无效果 hugePath.move(to: CGPoint(x: previousPathPosition.x, y: previousPathPosition.y)) hugePath.addLine(to: CGPoint(x: block.position.x, y: block.position.y)) let hugeShape = SCNShape(path: hugePath, extrusionDepth: 150.0) let hugeMaterial = SCNMaterial() hugeMaterial.diffuse.contents = UIColor.red hugeShape.materials = Array(repeating: hugeMaterial, count: 6) let hugeNode = SCNNode(geometry: hugeShape) hugeNode.position = SCNVector3(0.0, 0.0, 5.0) scnView.scene?.rootNode.addChildNode(hugeNode)
已知在UIBezierPath搭配CAShapeLayer的场景中,需要在图层而非路径上设置lineWidth,但该方案不适用于SceneKit。
补充需求:需要用实心3D路径连接一系列点。曾尝试复制路径并平移来模拟线宽,再合并路径,但最终得到两条带空隙的平行线,无法形成单一实心形状:
// 复制路径 let hugePathTopPart = hugePath.copy() as? UIBezierPath // 平移副本模拟线宽 hugePathTopPart?.apply(CGAffineTransform(translationX: 0.0, y: 40.0)) // 合并路径 hugePath.append(hugePathTopPart!)
解决方案
SceneKit的SCNShape是基于路径的填充区域挤压生成3D模型的,UIBezierPath的lineWidth只是2D绘制时的描边宽度,不会改变路径本身的轮廓。要生成实心粗3D线条,需将无面积的线条路径转换为带宽度的闭合填充路径。
方法1:利用strokedPath()快速生成闭合描边路径
UIBezierPath的strokedPath()方法可以根据当前路径的lineWidth、端点样式等属性,生成一个代表描边效果的闭合路径,该路径有实际填充面积,适合直接创建SCNShape。
修改后的代码:
let hugePath = UIBezierPath() hugePath.lineWidth = 40.0 // 此处lineWidth会生效,用于生成描边路径 hugePath.lineCapStyle = .round // 可选:设置线条端点为圆角,让3D线条两端更圆润 hugePath.lineJoinStyle = .round // 可选:多段线时设置拐角为圆角 hugePath.move(to: CGPoint(x: previousPathPosition.x, y: previousPathPosition.y)) hugePath.addLine(to: CGPoint(x: block.position.x, y: block.position.y)) // 关键:将原路径转换为带宽度的闭合路径 let strokedPath = hugePath.strokedPath() let hugeShape = SCNShape(path: strokedPath, extrusionDepth: 150.0) let hugeMaterial = SCNMaterial() hugeMaterial.diffuse.contents = UIColor.red hugeShape.materials = Array(repeating: hugeMaterial, count: 6) let hugeNode = SCNNode(geometry: hugeShape) hugeNode.position = SCNVector3(0.0, 0.0, 5.0) scnView.scene?.rootNode.addChildNode(hugeNode)
方法2:手动构建闭合多边形(自定义性更强)
如果需要更精细控制轮廓(比如自定义拐角、端点形状),可以手动计算线条的垂直偏移点,构建闭合矩形路径:
func createThickLinePath(from start: CGPoint, to end: CGPoint, lineWidth: CGFloat) -> UIBezierPath { let path = UIBezierPath() // 计算线条垂直方向的偏移量 let dx = end.x - start.x let dy = end.y - start.y let length = sqrt(dx*dx + dy*dy) let offsetX = -dy * (lineWidth/2) / length let offsetY = dx * (lineWidth/2) / length // 生成四个顶点:原线条上下偏移后的端点 let topStart = CGPoint(x: start.x + offsetX, y: start.y + offsetY) let topEnd = CGPoint(x: end.x + offsetX, y: end.y + offsetY) let bottomEnd = CGPoint(x: end.x - offsetX, y: end.y - offsetY) let bottomStart = CGPoint(x: start.x - offsetX, y: start.y - offsetY) // 构建闭合路径 path.move(to: topStart) path.addLine(to: topEnd) path.addLine(to: bottomEnd) path.addLine(to: bottomStart) path.close() return path } // 使用示例 let thickPath = createThickLinePath(from: previousPathPosition, to: block.position, lineWidth: 40.0) let hugeShape = SCNShape(path: thickPath, extrusionDepth: 150.0) // 后续材质、节点设置与原代码一致
这个方法生成的闭合矩形路径挤压后就是实心的粗3D线条,适合处理多段线连接的场景。
内容的提问来源于stack exchange,提问作者West1
相关产品推荐
相关产品推荐

