|
|
|
@@ -0,0 +1,228 @@
|
|
|
|
|
//
|
|
|
|
|
// World.swift
|
|
|
|
|
// Logic
|
|
|
|
|
//
|
|
|
|
|
// Created by Alex Loren on 8/26/26.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
import SwiftGodot
|
|
|
|
|
|
|
|
|
|
@Godot
|
|
|
|
|
class World: Node {
|
|
|
|
|
// MARK: - Properties
|
|
|
|
|
private var lightColors: [String] = [
|
|
|
|
|
"#95FDDE",
|
|
|
|
|
"#8029F8",
|
|
|
|
|
"#ED4881"
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
private var randomLightColor: Color {
|
|
|
|
|
get {
|
|
|
|
|
if let color = lightColors.randomElement(), Color.htmlIsValid(color: color) {
|
|
|
|
|
return Color.html(rgba: color)
|
|
|
|
|
}
|
|
|
|
|
print("Failed to get random light color, returning white.")
|
|
|
|
|
return .white
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MARK: - Functions
|
|
|
|
|
override func _enterTree() {
|
|
|
|
|
assemble()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func assemble() {
|
|
|
|
|
let camera = createCamera(at: Vector3(x: 0, y: -1.75, z: 4.5))
|
|
|
|
|
camera.environment = createEnvironment()
|
|
|
|
|
addChild(node: camera)
|
|
|
|
|
addChild(node: createRotatingWalls())
|
|
|
|
|
addChild(node: createPhysicsBounds())
|
|
|
|
|
|
|
|
|
|
for _ in 1...200 {
|
|
|
|
|
let sphere = createSphereRigidBody()
|
|
|
|
|
addChild(node: sphere)
|
|
|
|
|
sphere.globalPosition = getRandomPosition()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _ in 1...100 {
|
|
|
|
|
let box = createBoxRigidBody(size: 0.2)
|
|
|
|
|
addChild(node: box)
|
|
|
|
|
box.globalPosition = getRandomPosition()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for index in 0...2 {
|
|
|
|
|
let rotator = createNodeRotator()
|
|
|
|
|
let light = createLight(range: 15, color: Color.html(rgba: lightColors[index]))
|
|
|
|
|
rotator.addChild(node: light)
|
|
|
|
|
light.position = getRandomPosition(lower: 3, upper: 4)
|
|
|
|
|
addChild(node: rotator)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@discardableResult private func createCamera(at position: Vector3 = Vector3(x: 0, y: 0, z: 5)) -> Camera3D {
|
|
|
|
|
let camera = Camera3D()
|
|
|
|
|
camera.current = true
|
|
|
|
|
camera.position = position
|
|
|
|
|
return camera
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@discardableResult private func createRotatingWalls() -> Node3D {
|
|
|
|
|
let rotator = PhysicsRotator()
|
|
|
|
|
rotator.axis = Vector3(x: 1, y: 1, z: 1)
|
|
|
|
|
rotator.speed = 0.5
|
|
|
|
|
|
|
|
|
|
// Top and bottom walls
|
|
|
|
|
rotator.addChild(node: createCollidableWall(at: Vector3(x: 0, y: 2.5, z: 0)))
|
|
|
|
|
rotator.addChild(node: createCollidableWall(at: Vector3(x: 0, y: -2.5, z: 0)))
|
|
|
|
|
// Front and back walls
|
|
|
|
|
rotator.addChild(node: createCollidableWall(at: Vector3(x: 2.5, y: 0, z: 0), rotation: Vector3(x: 0, y: 0, z: 90)))
|
|
|
|
|
rotator.addChild(node: createCollidableWall(at: Vector3(x: -2.5, y: 0, z: 0), rotation: Vector3(x: 0, y: 0, z: 90)))
|
|
|
|
|
// Left and right walls
|
|
|
|
|
rotator.addChild(node: createCollidableWall(at: Vector3(x: 0, y: 0, z: 2.5), rotation: Vector3(x: 0, y: 90, z: 90)))
|
|
|
|
|
rotator.addChild(node: createCollidableWall(at: Vector3(x: 0, y: 0, z: -2.5), rotation: Vector3(x: 0, y: 90, z: 90)))
|
|
|
|
|
|
|
|
|
|
return rotator
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@discardableResult private func createCollidableWall(at position: Vector3, rotation: Vector3 = .zero) -> CollisionShape3D {
|
|
|
|
|
let shape = BoxShape3D()
|
|
|
|
|
shape.size = Vector3(x: 5, y: 0.5, z: 5)
|
|
|
|
|
let collider = CollisionShape3D()
|
|
|
|
|
collider.shape = shape
|
|
|
|
|
collider.position = position
|
|
|
|
|
collider.rotationDegrees = rotation
|
|
|
|
|
return collider
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@discardableResult private func createSphereRigidBody(radius: Double = 0.1, randomize: Bool = true, randomScale: Double = 0.05) -> RigidBody3D {
|
|
|
|
|
let definedRadius = !randomize ? radius : Double.random(in: (radius - randomScale)...(radius + randomScale))
|
|
|
|
|
let shape = SphereShape3D()
|
|
|
|
|
shape.radius = definedRadius
|
|
|
|
|
let collider = CollisionShape3D()
|
|
|
|
|
collider.shape = shape
|
|
|
|
|
|
|
|
|
|
let mesh = SphereMesh()
|
|
|
|
|
mesh.radius = definedRadius
|
|
|
|
|
mesh.height = definedRadius * 2
|
|
|
|
|
mesh.radialSegments = 8
|
|
|
|
|
mesh.rings = 8
|
|
|
|
|
let meshInstance = MeshInstance3D()
|
|
|
|
|
meshInstance.mesh = mesh
|
|
|
|
|
meshInstance.materialOverride = createMaterial()
|
|
|
|
|
|
|
|
|
|
let material = PhysicsMaterial()
|
|
|
|
|
material.bounce = 0.8
|
|
|
|
|
|
|
|
|
|
let body = RigidBody3D()
|
|
|
|
|
body.physicsMaterialOverride = material
|
|
|
|
|
body.addChild(node: collider)
|
|
|
|
|
body.addChild(node: meshInstance)
|
|
|
|
|
return body
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@discardableResult private func createBoxRigidBody(size: Float = 0.1, randomize: Bool = true, randomScale: Float = 0.05) -> RigidBody3D {
|
|
|
|
|
let definedSize = !randomize ? size : Float.random(in: (size - randomScale)...(size + randomScale))
|
|
|
|
|
let shape = BoxShape3D()
|
|
|
|
|
shape.size = Vector3(x: definedSize, y: definedSize, z: definedSize)
|
|
|
|
|
let collider = CollisionShape3D()
|
|
|
|
|
collider.shape = shape
|
|
|
|
|
|
|
|
|
|
let mesh = BoxMesh()
|
|
|
|
|
mesh.size = Vector3(x: definedSize, y: definedSize, z: definedSize)
|
|
|
|
|
let meshInstance = MeshInstance3D()
|
|
|
|
|
meshInstance.mesh = mesh
|
|
|
|
|
meshInstance.materialOverride = createMaterial()
|
|
|
|
|
|
|
|
|
|
let material = PhysicsMaterial()
|
|
|
|
|
material.bounce = 0.8
|
|
|
|
|
|
|
|
|
|
let body = RigidBody3D()
|
|
|
|
|
body.physicsMaterialOverride = material
|
|
|
|
|
body.addChild(node: collider)
|
|
|
|
|
body.addChild(node: meshInstance)
|
|
|
|
|
return body
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@discardableResult private func createLight(range: Double = 5, color: Color = .white) -> OmniLight3D {
|
|
|
|
|
let light = OmniLight3D()
|
|
|
|
|
light.omniRange = range
|
|
|
|
|
light.omniShadowMode = .cube
|
|
|
|
|
light.shadowEnabled = true
|
|
|
|
|
light.lightColor = color
|
|
|
|
|
light.lightEnergy = 5
|
|
|
|
|
return light
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@discardableResult private func createNodeRotator() -> NodeRotator {
|
|
|
|
|
let randomOnAxis: [Float] = [
|
|
|
|
|
Float(Int.random(in: -1...1)),
|
|
|
|
|
Float(Int.random(in: -1...1)),
|
|
|
|
|
Float(Int.random(in: -1...1))
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
let rotator = NodeRotator()
|
|
|
|
|
rotator.axis = Vector3(x: randomOnAxis[0], y: randomOnAxis[1], z: randomOnAxis[2])
|
|
|
|
|
rotator.speed = Double.random(in: 0.3...0.6)
|
|
|
|
|
return rotator
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private func createMaterial() -> StandardMaterial3D {
|
|
|
|
|
let material = StandardMaterial3D()
|
|
|
|
|
material.diffuseMode = .lambertWrap
|
|
|
|
|
return material
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@discardableResult private func createEnvironment() -> Environment {
|
|
|
|
|
let environment = Environment()
|
|
|
|
|
environment.backgroundMode = .color
|
|
|
|
|
environment.backgroundColor = .black
|
|
|
|
|
environment.glowEnabled = true
|
|
|
|
|
environment.glowIntensity = 0.6
|
|
|
|
|
environment.glowStrength = 1.75
|
|
|
|
|
return environment
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private func createPhysicsBounds() -> Area3D {
|
|
|
|
|
let shape = SphereShape3D()
|
|
|
|
|
shape.radius = 10
|
|
|
|
|
let collider = CollisionShape3D()
|
|
|
|
|
collider.shape = shape
|
|
|
|
|
let area = Area3D()
|
|
|
|
|
area.addChild(node: collider)
|
|
|
|
|
|
|
|
|
|
area.bodyExited.connect { [weak self] body in
|
|
|
|
|
guard let self, let body = body as? RigidBody3D else {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
body.getChildren().forEach { child in
|
|
|
|
|
if let collider = child as? CollisionShape3D {
|
|
|
|
|
switch collider.shape {
|
|
|
|
|
case is SphereShape3D:
|
|
|
|
|
let sphere = self.createSphereRigidBody()
|
|
|
|
|
addChild(node: sphere)
|
|
|
|
|
sphere.globalPosition = self.getRandomPosition()
|
|
|
|
|
break
|
|
|
|
|
case is BoxShape3D:
|
|
|
|
|
let box = self.createBoxRigidBody()
|
|
|
|
|
addChild(node: box)
|
|
|
|
|
box.globalPosition = self.getRandomPosition()
|
|
|
|
|
default:
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
body.queueFree()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return area
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private func getRandomPosition(in range: Float = 2) -> Vector3 {
|
|
|
|
|
return .init(x: Float.random(in: -range...range), y: Float.random(in: -range...range), z: Float.random(in: -range...range))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private func getRandomPosition(lower: Float = 1, upper: Float = 3) -> Vector3{
|
|
|
|
|
return .init(x: .random(in: lower...upper), y: .random(in: lower...upper), z: .random(in: lower...upper))
|
|
|
|
|
}
|
|
|
|
|
}
|