Skip to content

Commit

Permalink
adds PhysicsDirectSpaceState3D.IntersectRayResult and tests (#321)
Browse files Browse the repository at this point in the history
* adds PhysicsDirectSpaceState3D.IntersectRayResult and tests

* Update Sources/SwiftGodot/Extensions/PhysicsDirectSpaceState3D+IntersectRayResult.swift

* Update Sources/SwiftGodot/Extensions/PhysicsDirectSpaceState3D+IntersectRayResult.swift

* Update Sources/SwiftGodot/Extensions/PhysicsDirectSpaceState3D+IntersectRayResult.swift

* Update Sources/SwiftGodot/Extensions/PhysicsDirectSpaceState3D+IntersectRayResult.swift

* Update PhysicsDirectSpaceState3D+IntersectRayResult.swift

Documented fields.

---------

Co-authored-by: Miguel de Icaza <miguel@gnome.org>
  • Loading branch information
EstevanBR and migueldeicaza authored Jan 6, 2024
1 parent 372692c commit 96b2f63
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
//
// PhysicsDirectSpaceState3D+IntersectRayResult
//
//
// Created by Estevan Hernandez on 12/24/23.
//

private extension GDictionary {
func makeOrUnwrap<T: VariantStorable>(key: String) -> T? {
guard let variant = self[key] else {
GD.pushWarning("There was no Variant for key: \(key)")
return nil
}
guard let result = T.makeOrUnwrap(variant) else {
GD.pushWarning("\(T.self).makeOrUnwrap(\(variant)) was nil")
return nil
}

return result
}
}

extension PhysicsDirectSpaceState3D {
/// Result from intersecting a ray
public struct IntersectRayResult<T: Object> {
/// The intersection point
public let position: Vector3
/// The object's surface normal at the intersection point, or `Vector3(x: 0, y: 0, z: 0)` if the ray starts inside the shape and `PhysicsRayQueryParameters3D.hitFromInside` is true.
public let normal: Vector3
/// The colliding object
public let collider: T
/// The colliding object's ID.
public let colliderId: Int
/// The The intersecting object's ``RID``.
public let rid: RID
/// The shape index of the colliding shape.
public let shape: Int
/// The metadata value from the dictionary.
public let metadata: Variant?
/// The face index at the intersection point.
public let faceIndex: Int

init?(_ dictionary: GDictionary) {
guard dictionary.isEmpty() == false,
let position: Vector3 = dictionary.makeOrUnwrap(key: "position"),
let normal: Vector3 = dictionary.makeOrUnwrap(key: "normal"),
let colliderVariant = dictionary["collider"],
let collider = T.makeOrUnwrap(colliderVariant),
let colliderId: Int = dictionary.makeOrUnwrap(key: "collider_id"),
let rid: RID = dictionary.makeOrUnwrap(key: "rid"),
let shape: Int = dictionary.makeOrUnwrap(key: "shape"),
let faceIndex: Int = dictionary.makeOrUnwrap(key: "face_index") else {
return nil
}
self.position = position
self.normal = normal
self.collider = collider
self.colliderId = colliderId
self.rid = rid
self.shape = shape
self.faceIndex = faceIndex
self.metadata = dictionary["metadata"]
}
}
}

extension PhysicsDirectSpaceState3D {
/// Intersects a ray in a given space. Ray position and other parameters are defined through `PhysicsRayQueryParameters3D` The return value is an `IntersectRayResult<T>?` where `T` is any Godot `Object`, however if the ray did not intersect anything, or the intersecting collider was not of type `T` then a nil object is returned instead. Usually `T` is a physics object such as `StaticBody` for example but it could also be a `GridMap` if the `mesh_library` has collisions.
public func intersectRay<T: Object>(_ type: T.Type = T.self, parameters: PhysicsRayQueryParameters3D) -> IntersectRayResult<T>? {
let dictionary: GDictionary = intersectRay(parameters: parameters)
return IntersectRayResult<T>(dictionary)
}
}
58 changes: 58 additions & 0 deletions Tests/SwiftGodotTests/IntersectRayResultTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
//
// IntersectRayResultTests.swift
// SwiftGodotTests
//
// Created by Estevan Hernandez on 12/24/23.
//

import XCTest
import SwiftGodotTestability
@testable import SwiftGodot

final class IntersectRayResultTests: GodotTestCase {
func testIntersectRayResultPropertiesMatchDictionary_whenAllPropertiesPresent() throws {
let collider: Object = GridMap()

let dictionary: GDictionary = {
let dictionary = GDictionary()
dictionary["position"] = Variant(Vector3(x: 1, y: 2, z: 3))
dictionary["normal"] = Variant(Vector3(x: 4, y: 5, z: 6))
dictionary["collider"] = Variant(collider)
dictionary["collider_id"] = Variant(collider.id)
dictionary["rid"] = Variant(RID())
dictionary["shape"] = Variant(22)
dictionary["face_index"] = Variant(44)
return dictionary
}()

let result = try XCTUnwrap(PhysicsDirectSpaceState3D.IntersectRayResult<GridMap>(dictionary))

XCTAssertEqual(result.position, Vector3(x: 1, y: 2, z: 3))
XCTAssertEqual(result.normal, Vector3(x: 4, y: 5, z: 6))
XCTAssertEqual(result.collider, collider)
XCTAssertEqual(result.colliderId, collider.id)
XCTAssertEqual(result.rid, RID())
XCTAssertEqual(result.shape, 22)
XCTAssertEqual(result.faceIndex, 44)
}

func testIntersectRayResultIsNil_whenColliderPropertyIsMissing() {
let collider: Object = GridMap()

let dictionary: GDictionary = {
let dictionary = GDictionary()
dictionary["position"] = Variant(Vector3(x: 1, y: 2, z: 3))
dictionary["normal"] = Variant(Vector3(x: 4, y: 5, z: 6))
// dictionary["collider"] = Variant(collider)
dictionary["collider_id"] = Variant(collider.id)
dictionary["rid"] = Variant(RID())
dictionary["shape"] = Variant(22)
dictionary["face_index"] = Variant(44)
return dictionary
}()

let result = PhysicsDirectSpaceState3D.IntersectRayResult<GridMap>(dictionary)

XCTAssertNil(result)
}
}

0 comments on commit 96b2f63

Please sign in to comment.