ARKit - Как содержать SCNText в другом SCNNode (речевой пузырь)
Я пытаюсь создать генератор цитат с простым текстом в речевом пузыре в ARKit.
Я могу показать речевой пузырь с текстом, но текст всегда начинается посередине и выходит за пределы речевого пузыря.
Мы будем благодарны за любую помощь в выравнивании его в левом верхнем углу речевого пузыря и обтекании внутри речевого пузыря.
Результат
Классы
class SpeechBubbleNode: SCNNode {
private let textNode = TextNode()
var string: String? {
didSet {
textNode.string = string
}
}
override init() {
super.init()
// Speech Bubble
let plane = SCNPlane(width: 200.0, height: 100.0)
plane.cornerRadius = 4.0
plane.firstMaterial?.isDoubleSided = true
geometry = plane
// Text Node
textNode.position = SCNVector3(position.x, position.y, position.z + 1.0)
// textNode.position = convertPosition(SCNVector3(0.0, 0.0, 1.0), to: textNode)
// textNode.position = SCNVector3(0.0, 0.0, position.z + 1.0)
addChildNode(textNode)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
class TextNode: SCNNode {
private let textGeometry = SCNText()
var string: String? {
didSet {
updateTextContainerFrame()
textGeometry.string = string
}
}
override init() {
super.init()
textGeometry.truncationMode = CATextLayerTruncationMode.middle.rawValue
textGeometry.isWrapped = true
textGeometry.alignmentMode = CATextLayerAlignmentMode.left.rawValue
let blackMaterial = SCNMaterial()
blackMaterial.diffuse.contents = UIColor.black
blackMaterial.locksAmbientWithDiffuse = true
textGeometry.materials = [blackMaterial]
geometry = textGeometry
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
private func updateTextContainerFrame() {
let (min, max) = boundingBox
let width = CGFloat(max.x - min.x)
let height = CGFloat(max.y - min.y)
print("width :",max.x - min.x,"height :",max.y - min.y,"depth :",max.z - min.z)
textGeometry.containerFrame = CGRect(x: 0.0, y: 0.0, width: width, height: height)
// textGeometry.containerFrame = CGRect(origin: .zero, size: CGSize(width: 1.0, height: 1.0))
}
}
Реализация
private func makeSpeechBubbleNode(forBobbleheadNode bobbleheadNode: BobbleheadNode) {
let node = SpeechBubbleNode()
node.position = sceneView.scene.rootNode.convertPosition(bobbleheadNode.position, to: node)
node.scale = SCNVector3(0.002, 0.002, 0.002)
sceneView.scene.rootNode.addChildNode(speechBubbleNode)
self.speechBubbleNode = speechBubbleNode
speechBubbleNode.string = "Some random string that could be long and should wrap within speech bubble"
}
2 ответа
У меня была та же проблема, и наконец я решил ее следующим образом:
Создайте SCNText и добавьте его как геометрию в SCNNode:
let string = "Coverin text with a plane :)" let text = SCNText(string: string, extrusionDepth: 0.1) text.font = UIFont.systemFont(ofSize: 1) text.flatness = 0.005 let textNode = SCNNode(geometry: text) let fontScale: Float = 0.01 textNode.scale = SCNVector3(fontScale, fontScale, fontScale)
Координируйте форму сводки текста слева внизу по центру:
let (min, max) = (text.boundingBox.min, text.boundingBox.max) let dx = min.x + 0.5 * (max.x - min.x) let dy = min.y + 0.5 * (max.y - min.y) let dz = min.z + 0.5 * (max.z - min.z) textNode.pivot = SCNMatrix4MakeTranslation(dx, dy, dz)
Создайте PlaneNode и добавьте textNode как childNode для PlaneNode:
let width = (max.x - min.x) * fontScale let height = (max.y - min.y) * fontScale let plane = SCNPlane(width: CGFloat(width), height: CGFloat(height)) let planeNode = SCNNode(geometry: plane) planeNode.geometry?.firstMaterial?.diffuse.contents = UIColor.green.withAlphaComponent(0.5) planeNode.geometry?.firstMaterial?.isDoubleSided = true planeNode.position = textNode.position textNode.eulerAngles = planeNode.eulerAngles planeNode.addChildNode(textNode)
и в конце добавьте PlaneNode в sceneView:
sceneView.scene.rootNode.addChildNode(planeNode)
и вот результат:
Если вам нужен только белый квадрат за вашим текстом, я достиг этого, выполнив это в моей функции рендерера:
func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode? {
let node = SCNNode()
............
let testPlane = SCNPlane(width: someWidth, height: someHeight)
let testScene = SKScene(size: CGSize(width: 900, height: 900))
testScene.backgroundColor = UIColor.white
let str = SKLabelNode(text: "This is just a test")
str.color = UIColor.black
str.fontColor = UIColor.black
str.fontSize = 45.5
str.position = CGPoint(x: stuff.size.width / 2,
y: stuff.size.height / 2)
testScene.addChild(str)
testPlane.firstMaterial?.diffuse.contents = testScene
testPlane.firstMaterial?.isDoubleSided = true
testPlane.firstMaterial?.diffuse.contentsTransform = SCNMatrix4Translate(SCNMatrix4MakeScale(1, -1, 1), 0, 1, 0)
let testNode = SCNNode(geometry: testPlane)
testNode.eulerAngles.x = -.pi / 2
testNode.position = SCNVector3Make(0.0,0.0,0.0)
node.addChildNode(testNode)
}