diff --git a/Swift/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata b/Swift/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/Swift/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Swift/Package.swift b/Swift/Package.swift index 3ba7275..13daaee 100644 --- a/Swift/Package.swift +++ b/Swift/Package.swift @@ -3,9 +3,10 @@ import PackageDescription let package = Package( - name: "logic", + name: "Logic", platforms: [ - .macOS(.v14) + .macOS(.v14), + .iOS(.v17) ], products: [ .library( diff --git a/Swift/Sources/Logic/Logic.swift b/Swift/Sources/Logic/Logic.swift index 78f60ba..1cd79b0 100644 --- a/Swift/Sources/Logic/Logic.swift +++ b/Swift/Sources/Logic/Logic.swift @@ -1,2 +1,14 @@ -import Foundation +// +// Logic.swift +// Logic +// +// Created by Alex Loren on 8/26/26. +// + import SwiftGodot + +#initSwiftExtension(cdecl: "swift_entry_point", types: [ + NodeRotator.self, + PhysicsRotator.self, + World.self +]) diff --git a/Swift/Sources/Logic/Nodes/NodeRotator.swift b/Swift/Sources/Logic/Nodes/NodeRotator.swift new file mode 100644 index 0000000..9790415 --- /dev/null +++ b/Swift/Sources/Logic/Nodes/NodeRotator.swift @@ -0,0 +1,20 @@ +// +// NodeRotator.swift +// Logic +// +// Created by Alex Loren on 8/26/26. +// + +import SwiftGodot + +@Godot +class NodeRotator: Node3D { + // MARK: - Properties + public var axis: Vector3 = .zero + public var speed: Double = 5 + + // MARK: - Functions + public override func _physicsProcess(delta: Double) { + rotate(axis: axis.normalized(), angle: speed * delta) + } +} diff --git a/Swift/Sources/Logic/Nodes/PhysicsRotator.swift b/Swift/Sources/Logic/Nodes/PhysicsRotator.swift new file mode 100644 index 0000000..aaa9182 --- /dev/null +++ b/Swift/Sources/Logic/Nodes/PhysicsRotator.swift @@ -0,0 +1,20 @@ +// +// PhysicsRotator.swift +// Logic +// +// Created by Alex Loren on 8/26/26. +// + +import SwiftGodot + +@Godot +class PhysicsRotator: AnimatableBody3D { + // MARK: - Properties + public var axis: Vector3 = .zero + public var speed: Double = 5 + + // MARK: - Functions + public override func _physicsProcess(delta: Double) { + rotate(axis: axis.normalized(), angle: speed * delta) + } +} diff --git a/Swift/Sources/Logic/Nodes/World.swift b/Swift/Sources/Logic/Nodes/World.swift new file mode 100644 index 0000000..b4e6d7d --- /dev/null +++ b/Swift/Sources/Logic/Nodes/World.swift @@ -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)) + } +}