[Mac M2 Pro 12CPU, Ventura 13.6, visionOS 1.0 (21N5300a), Xcode 15.1 beta 3]
ランチャーアプリvisionOS版を開発しています。
3Dモデルの各面および面取り領域のカラーを自由に変更できるようにしました。
予定の機能は全て実装完了となり、これで一区切りとします。

[Mac M2 Pro 12CPU, Ventura 13.6, visionOS 1.0 (21N5300a), Xcode 15.1 beta 3]
ランチャーアプリvisionOS版を開発しています。
3Dモデルの各面および面取り領域のカラーを自由に変更できるようにしました。
予定の機能は全て実装完了となり、これで一区切りとします。
[Mac M2 Pro 12CPU, Ventura 13.6, visionOS 1.0 (21N5300a), Xcode 15.1 beta 3]
usdzファイルを読み込んだModelEntityは後からコードでカラー変更できるようです。あくまでアプリ上で一時的に変わるだけで、元モデルの色は変わりません。
マテリアルのインデックス番号を指定して、3Dモデルの各面や面取り領域のカラーを変えることも可能でしょう。
Reality Composer Proの機能をコード化しているといったところでしょうか。
var body: some View {
ZStack {
RealityView { content in
if let entity = try? await ModelEntity(named: "hexa") {
// エンティティのモデルを取得
if var modelComponent = entity.model {
for i in 0..<modelComponent.materials.count {
if var material = modelComponent.materials[i] as? PhysicallyBasedMaterial {
// 全てのマテリアルをグリーンにする
material.baseColor = PhysicallyBasedMaterial.BaseColor(tint: .green)
// マテリアルを更新
modelComponent.materials[i] = material
}
}
// エンティティのモデルコンポーネントを更新
entity.model = modelComponent
}
[Mac M2 Pro 12CPU, Ventura 13.6, visionOS 1.0 (21N5300a), Xcode 15.1 beta 3]
スライダーで3Dモデルの回転速度を変えられるようにしました。
スライダーを動かして止めると同時に変数、SwiftData、Componentの値を更新する仕組みの構築に苦労しました。
回転速度を設定しても他の画面に遷移して設定画面に戻るとスライダーが初期値になっている(回転速度自体はそのまま)、スライダーが初期値から動かない(動かしても離すと戻る)など様々なトラブルに見舞われ、結局丸一日掛かってしまいました。
[Mac M2 Pro 12CPU, Ventura 13.6, visionOS 1.0 (21N5300a), Xcode 15.1 beta 3]
AVSpeechUtteranceなどを使ってテキスト読み上げをさせているのですが、空文字読み上げで簡単にクラッシュします。その場合はアプリ再起動で直ります。
今度は読み上げスピードを1.5に設定するとクラッシュしました。こちらは重症でシミュレータを再インストールして復活しました。
Apple Developer ForumsやStack Overflowではこのトラブルにコードで何とかしようと悪戦苦闘されていますが、そもそも環境が壊れているので、シミュレータであれば再インストールで解決する事例でした。iPhoneなど実機の場合は解決方法不明です。
import AVFoundation
let speechSynthesizer = AVSpeechSynthesizer()
let utterance: AVSpeechUtterance
var textToSpeak: String = "XXX"
utterance = AVSpeechUtterance(string: textToSpeak)
utterance.voice = AVSpeechSynthesisVoice(language: "en-US")
utterance.rate = 1.5 // これを追加するとクラッシュした
speechSynthesizer.speak(utterance)
[Mac M2 Pro 12CPU, Ventura 13.6, visionOS 1.0 (21N5300a), Xcode 15.1 beta 3]
SwiftDataの特徴をようやくつかめてきたので、書き留めておきます。
SwiftDataは見た目は配列っぽいですが、中身はデータベースそのものです。配列として扱うと痛い目にあいます。
SwiftDataのデータモデルにデータを追加しても、配列の末尾に配置されるとは限りません。常に何らかのキーでソートしておく必要があります。そのため通し番号あるいは日時のプロパティは必須です。
import Foundation
import SwiftUI
import SwiftData
@Model
final class Face {
var appNum: Int
var appName: String
var urlScheme: String
var createDate: String
init(appNum: Int, appName: String, urlScheme: String, createDate: String) {
self.appNum = appNum
self.appName = appName
self.urlScheme = urlScheme
self.createDate = createDate
}
}
import SwiftUI
import RealityKitContent
import SwiftData
@main
struct TestApp: App {
init(){
RotateComponent.registerComponent()
RotateSystem.registerSystem()
}
var body: some Scene {
WindowGroup {
ContentView()
.modelContainer(for: [Face.self, AppColor.self])
}
.windowStyle(.volumetric)
.defaultSize(width: 1.0, height: 1.2, depth: 0.3, in: .meters)
ImmersiveSpace(id: "ImmersiveSpace") {
ImmersiveView()
}
.immersionStyle(selection: .constant(.full), in: .full)
}
}
import SwiftUI
import RealityKit
import RealityKitContent
import SwiftData
struct ContentView: View {
// 常に生成日時で昇順ソート
@Query(sort: \Face.createDate) public var faces: [Face]
private func updateApp() {
var nums: [Int] = [1,2,3,4,5,6]
let size = faces.count
var index = size - 1
print("size: \(size)")
// 配列の末尾からfor文を回す(新しいデータのみ残すため)
for face in faces.reversed() {
print("index: \(index)")
print("face.appNum: \(face.appNum)")
print("face.appName: \(face.appName)")
print("face.createDate: \(face.createDate)")
let surNum = face.appNum
if nums != [], nums.contains(surNum){
nums.removeAll { $0 == surNum }
print("\(surface.appNum)をnumsから削除しました")
print("現在のnums: \(nums)")
print("\(surface.appNum)の最初のsurfaceは残留しました")
} else {
context.delete(faces[index])
try? context.save()
print("index\(index)を削除しました")
}
index -= 1
if index == -1 {
print("faces整理後確認")
var count = 0
for face in faces{
print("faces整理後 count: \(count)")
print("face.appNum: \(face.appNum)")
print("face.appName: \(face.appName)")
print("face.urlScheme: \(face.urlScheme)")
print("face.createDate: \(face.createDate)")
count += 1
}
}
}
}
[Mac M2 Pro 12CPU, Ventura 13.6, visionOS 1.0 (21N5300a), Xcode 15.1 beta 3]
SwiftDataで使うモデルにプロパティを追加すると、シミュレータ操作時に以下のエラーが出るようになりました。
import Foundation
import SwiftUI
import SwiftData
@Model
final class Face {
var appNum: Int
var appName: String
var urlScheme: String
var createDate: String // 追加したプロパティ
init(appNum: Int, appName: String, urlScheme: String, createDate: String) {
self.appNum = appNum
self.appName = appName
self.urlScheme = urlScheme
self.createDate = createDate
}
}
Fatal error: failed to find a currently active container
Apple Developer Forumsでも問題になっていて、アプリを再インストールすると直るという結論に落ち着いていました。
visionOSシミュレータからアプリを削除する方法が分からなかったので、メニューからDevice – Erase All Content and Settings… を選択してリセットしました。
23/12/01追記
アプリのアイコン長押しで削除できることを思い出しました。
[Mac M2 Pro 12CPU, Ventura 13.6, visionOS 1.0 (21N5300a), Xcode 15.1 beta 3]
visionOSで使える簡単なユーティリティアプリを製作しています。
iOS 17から利用可能になったSwiftDataを試しています。まだ基本的な操作しか修得していませんが、まずまずの感触です。
現段階で大方の機能は実装しました。あとは自動回転などお遊び機能を入れてみようかと思います。
[Mac M2 Pro 12CPU, Ventura 13.6, visionOS 1.0 (21N5300a), Xcode 15.1 beta 3]
ようやく3Dモデルへのタップを検知できるようになりました。
11/4にタップ検知実装に着手してから3週間も掛かりました。あまりの開発効率の悪さに我ながら引いています。
解決後、よく調べてみるとAppleが提供しているサンプルコード HappyBeamでCollisionComponentを扱っていました。
単なるEntityではなくusdzファイルを読み込ませたModelEntityでないと上手くいかないようです。デフォルトのSceneエンティティはただのEntityなのでハナから無理筋だったのか。
UnityでもvisionOSアプリを開発できるものの、Unity Pro(年額26.8万円)の登録が必須になっています。2年分でVision Proを購入できます。まあ個人開発者には厳しいですね。
そのうちUnity Personal(無料)に登録してMeta Quest用アプリを作ってみたいですが、visionOSアプリ開発にしばらく注力します。macOSやWindowsのように複数のアプリを起動できるvisionOSの方が私には魅力的です。
[Mac M2 Pro 12CPU, Ventura 13.6, visionOS 1.0 (21N5300a), Xcode 15.1 beta 3]
visionOSアプリを製作しています。
Entity Component Systemアーキテクチャ(ECS)の扱いに手間取っています。初めはReality Composer Proを使ってRealityKitContent内でECSを一元管理しようと試みましたが、ContentViewにあるボタンとの連携がうまく出来ません。そこで自製ComponentについてはPackages外にECSディレクトリを設けて別管理することにしました。
[Mac M2 Pro 12CPU, Ventura 13.6, Python 3.10.4]
DALL-E 3からAPIで使えるようになったので、早速試してみました。
生駒山近景を出力したところ存在しないロープウェイを含む景色でした。2回目は信仰の山らしく祠の画像でした。六甲山も出してみました。
どうも画のタッチが好みではありません。そこら辺を調整できれば、使い出がありそうです。
# openaiライブラリを導入済みの場合はアップグレードする(旧Verにはopenai.OpenAIがない)
pip install --upgrade openai
import os
from openai import OpenAI
# 環境変数からAPIキーを取得
api_key = os.getenv('CHATGPT_API_KEY')
if not api_key:
raise ValueError("OpenAI API Keyが環境変数に設定されていません")
# APIキーを設定
os.environ["OPENAI_API_KEY"] = api_key
client = OpenAI()
response = client.images.generate(
model="dall-e-3",
prompt="大阪府・奈良県にある生駒山の近景",
size="1024x1024",
quality="standard",
n=1,
)
image_url = response.data[0].url
print(image_url)