Last active
December 8, 2024 23:50
-
-
Save radicalappdev/77ba6ebc2cfc76e8806625c3d10340f1 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// File.swift | |
// RealityKitContent | |
// | |
// Created by Joseph Simpson on 12/8/24. | |
// Thanks Cursor and Claude! | |
import Foundation | |
@preconcurrency import RealityKit | |
public struct EntitySpawnerComponent: Component, Codable { | |
public enum SpawnShape: String, Codable { | |
case domeUpper | |
case domeLower | |
case sphere | |
case box | |
case plane | |
case circle | |
} | |
/// The number of clones to create | |
public var Copies: Int = 12 | |
/// The shape to spawn entities in | |
public var SpawnShape: SpawnShape = .domeUpper | |
/// Radius for spherical shapes (dome, sphere, circle) | |
public var Radius: Float = 5.0 | |
/// Dimensions for box spawning (width, height, depth) | |
public var BoxDimensions: SIMD3<Float> = [2.0, 2.0, 2.0] | |
/// Dimensions for plane spawning (width, depth) | |
public var PlaneDimensions: SIMD2<Float> = [2.0, 2.0] | |
/// Track if we've already spawned copies | |
public var HasSpawned: Bool = false | |
public init() { | |
} | |
} | |
public class EntitySpawnerSystem: System { | |
// Define a query to return all entities with a EntitySpawnerComponent. | |
private static let query = EntityQuery(where: .has(EntitySpawnerComponent.self)) | |
// init is required even when not used | |
required public init(scene: Scene) { | |
// Perform required initialization or setup. | |
} | |
private func positionForShape(_ shape: EntitySpawnerComponent.SpawnShape, | |
component: EntitySpawnerComponent) -> SIMD3<Float> { | |
switch shape { | |
case .domeUpper: | |
let distance = Float.random(in: 1...component.Radius) | |
let theta = Float.random(in: 0...(2 * .pi)) | |
let phi = Float.random(in: 0...(Float.pi / 2)) | |
return SIMD3( | |
distance * sin(phi) * cos(theta), | |
distance * cos(phi), | |
distance * sin(phi) * sin(theta) | |
) | |
case .domeLower: | |
let distance = Float.random(in: 1...component.Radius) | |
let theta = Float.random(in: 0...(2 * .pi)) | |
let phi = Float.random(in: (Float.pi / 2)...Float.pi) | |
return SIMD3( | |
distance * sin(phi) * cos(theta), | |
distance * cos(phi), | |
distance * sin(phi) * sin(theta) | |
) | |
case .sphere: | |
let distance = Float.random(in: 1...component.Radius) | |
let theta = Float.random(in: 0...(2 * .pi)) | |
let phi = Float.random(in: 0...Float.pi) | |
return SIMD3( | |
distance * sin(phi) * cos(theta), | |
distance * cos(phi), | |
distance * sin(phi) * sin(theta) | |
) | |
case .box: | |
let dims = component.BoxDimensions * 0.5 // Convert to +/- dimensions | |
return SIMD3( | |
Float.random(in: -dims.x...dims.x), | |
Float.random(in: -dims.y...dims.y), | |
Float.random(in: -dims.z...dims.z) | |
) | |
case .plane: | |
let dims = component.PlaneDimensions * 0.5 // Convert to +/- dimensions | |
return SIMD3( | |
Float.random(in: -dims.x...dims.x), | |
0, | |
Float.random(in: -dims.y...dims.y) | |
) | |
case .circle: | |
let angle = Float.random(in: 0...(2 * .pi)) | |
let randomRadius = Float.random(in: 0...component.Radius) | |
return SIMD3( | |
randomRadius * cos(angle), | |
0, | |
randomRadius * sin(angle) | |
) | |
} | |
} | |
public func update(context: SceneUpdateContext) { | |
for entity in context.entities( | |
matching: Self.query, | |
updatingSystemWhen: .rendering | |
) { | |
guard var spawnerComponent = entity.components[EntitySpawnerComponent.self] else { continue } | |
if spawnerComponent.HasSpawned { continue } | |
for _ in 1...spawnerComponent.Copies { | |
let clone = entity.clone(recursive: true) | |
clone.components.remove(EntitySpawnerComponent.self) | |
// Calculate offset in local space | |
let localOffset = positionForShape(spawnerComponent.SpawnShape, component: spawnerComponent) | |
// Convert the local offset to world space using the entity's transform | |
let worldTransform = entity.transform | |
let rotatedOffset = worldTransform.rotation.act(localOffset) | |
clone.position = entity.position + (rotatedOffset * worldTransform.scale) | |
// Inherit orientation from parent | |
clone.orientation = entity.orientation | |
entity.parent?.addChild(clone) | |
} | |
spawnerComponent.HasSpawned = true | |
entity.components[EntitySpawnerComponent.self] = spawnerComponent | |
} | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment