I am trying to create a quote generator with simple text within a speech bubble in ARKit.
I can show the speech bubble with text, but the text always starts in the middle and overflows outside of the speech bubble.
Any help getting it align in the top left of the speech bubble and wrapping within the speech bubble would be appreciated.
Result
Classes
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))
}
}
Implementation
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"
}
I had the same problem and finally I have solved it as following:
Create a SCNText and add it as a geometry to 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)
Coordinate the text pivot form left bottom to center:
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)
Create a PlaneNode and add the textNode as a childNode of the 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)
and at the end add the PlaneNode to sceneView:
sceneView.scene.rootNode.addChildNode(planeNode)
and that's the result:
If you only need a white box behind your text I achieved it by doing this in my renderer function:
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)
}
来源:https://stackoverflow.com/questions/50731775/arkit-how-to-contain-scntext-within-another-scnnode-speech-bubble