Reputation: 41
I'm new to SwiftUI and RealityKit and I was wondering how I can change the color of an entity with a transition. Here's some code I wrote to change the color of a cube when it is tapped:
import SwiftUI
import RealityKit
import RealityKitContent
struct ContentView: View {
@State var color = SimpleMaterial.Color(.red)
private let colors: [SimpleMaterial.Color] = [.red, .green, .blue, .orange, .yellow]
static var cubeEntity = Entity()
var body: some View {
VStack {
RealityView { content in
// Add the initial RealityKit content
if let cube = try? await Entity(named: "MyCube", in: realityKitContentBundle) {
content.add(cube)
ContentView.cubeEntity = cube
}
} update: { content in
let selectedMaterial = SimpleMaterial(color: color, isMetallic: false)
let modelEntity = ContentView.cubeEntity.findEntity(named: "MyCube") as! ModelEntity
// how can I add a transition to this?
modelEntity.model?.materials[0] = selectedMaterial
}
.gesture(SpatialTapGesture().targetedToAnyEntity().onEnded { _ in
color = colors.randomElement()!
})
}
}
}
#Preview(windowStyle: .volumetric) {
ContentView()
}
I'm also not sure if the way I'm targeting the cube to change the color is aligned with best practices so would love some feedback on that if there's a better way!
Upvotes: 1
Views: 251
Reputation: 58553
In RealityKit 4.0, the easiest way to change the color of any model using a transition is to implement a video material that can play short videos (1 sec) with the rendered transition. I've made three video files (14kB each) for sphere primitive
in Apple Motion app. If you want to use a cube instead, then I recommend you create a custom cube in a 3D app, since you can only apply a texture to RealityKit's box primitive on a per-face-basis (not to the entire object).
Here's my code:
import SwiftUI
import RealityKit
import AVKit
struct ContentView : View {
@State private var player: AVPlayer? = nil
@State private var index: Int = 0
let sphere = ModelEntity(mesh: .generateSphere(radius: 0.5))
let videos = ["BluToOra", "OraToGre", "GreToBlu"]
var body: some View {
RealityView { rvc in
sphere.generateCollisionShapes(recursive: false)
sphere.components.set(InputTargetComponent())
rvc.add(sphere)
}
.onTapGesture {
if let url = Bundle.main.url(forResource: videos[index % 3],
withExtension: "mov") {
player = AVPlayer(url: url)
player?.play()
sphere.model?.materials = [VideoMaterial(avPlayer: player!)]
}
index += 1
}
}
}
At the moment there's one drawback: the model blinks when the next video file is loading.
Upvotes: 0