I've run this Git. What I'm trying to do is when the node is further than the distance I set, for example 1 meter, node is removed. With in it, I've added this method (Code 1).
Code 1
func getDistanceBtw(cameraPostion: SCNVector3, nodePosition: SCNVector3) -> Float {
let powX = (cameraPostion.x - nodePosition.x) * (cameraPostion.x - nodePosition.x)
let powY = (cameraPostion.y - nodePosition.y) * (cameraPostion.y - nodePosition.y)
let powZ = (cameraPostion.z - nodePosition.z) * (cameraPostion.z - nodePosition.z)
let powXYZ = powX + powY + powZ
let distance = sqrt(powXYZ)
return distance
}
And in renderer didUpdate Node method, I added... (Code 2)
Code 2
let cameraPostion = sceneView.pointOfView?.position
let nodePosition = node.position
if getDistanceBtw(cameraPostion: cameraPostion!,
nodePosition: nodePosition) > 1 {
node.removeFromParentNode()
}
I thought this my solve what I want to achieve, it did something but not what I wanted.
Related
I am trying to create some AR experience.
I load the Model with animations as an Entity. Lets call it a Toy.
I create an AnchorEntity.
I attach the Toy to the AnchorEntity. Up to this point everything works great.
I want the Toy to walk in random directions. And it does for the first time. Then it gets interesting, allow me to share my code:
First method uses a newly created Transform for the Toy with the modified translation x, y, to make the Toy move and that is it.
func walk(completion: #escaping () -> Void) {
guard let robot = robot else {
return
}
let currentTransform = robot.transform
guard let path = randomPath(from: currentTransform) else {
return
}
let (newTranslation , travelTime) = path
let newTransform = Transform(scale: currentTransform.scale,
rotation: currentTransform.rotation,
translation: newTranslation)
robot.move(to: newTransform, relativeTo: nil, duration: travelTime)
DispatchQueue.main.asyncAfter(deadline: .now() + travelTime + 1) {
completion()
}
}
We get that new Transform from the method below.
func randomPath(from currentTransform: Transform) -> (SIMD3<Float>, TimeInterval)? {
// Get the robot's current transform and translation
let robotTranslation = currentTransform.translation
// Generate random distances for a model to cross, relative to origin
let randomXTranslation = Float.random(in: 0.1...0.4) * [-1.0,1.0].randomElement()!
let randomZTranslation = Float.random(in: 0.1...0.4) * [-1.0,1.0].randomElement()!
// Create a translation relative to the current transform
let relativeXTranslation = robotTranslation.x + randomXTranslation
let relativeZTranslation = robotTranslation.z + randomZTranslation
// Find a path
var path = (randomXTranslation * randomXTranslation + randomZTranslation * randomZTranslation).squareRoot()
// Path only positive
if path < 0 { path = -path }
// Calculate the time of walking based on the distance and default speed
let timeOfWalking: Float = path / settings.robotSpeed
// Based on old trasnlation calculate the new one
let newTranslation: SIMD3<Float> = [relativeXTranslation,
Float(0),
relativeZTranslation]
return (newTranslation, TimeInterval(timeOfWalking))
}
The problem is that the value of Entity.transform.translation.y grows from 0 to some random value < 1. Always after the second time the walk() method is being called.
As you can see, every time the method is called, newTranslation sets the Y value to be 0. And yet the Toy's translation:
I am out of ideas any help is appreciated. I can share the whole code if needed.
I have managed to fix the issue by specifying parameter relativeTo as Toy's AnchorEntity:
toy.move(to: newTransform, relativeTo: anchorEntity, duration: travelTime)
I have an app that that aims to record the camera's current X,Y,Z coordinates and print them out. It does this properly with the code down below
func renderer(_ renderer: SCNSceneRenderer, willRenderScene scene: SCNScene, atTime time: TimeInterval) {
guard let pointOfView = sceneView.pointOfView else { return }
let transform = pointOfView.transform
let CamPosition = SCNVector3(transform.m41, transform.m42, transform.m43)
print(CamPosition)
I want to truncate the printed output since it's very long. I found this extension to truncate the values.
extension Double {
func truncate(places : Int)-> Double {
return Double(floor(pow(10.0, Double(places)) * self)/pow(10.0, Double(places)))
}
}
This works if I print something like this:
x = 1.123456789
print(x.truncate(places: 2))
but will not work if I print it out like this:
print(camRotation.truncate(palces:2))
The error it gives me says "Value of type 'SCNVector4' has no member 'truncate'"
is this a formatting issue on my end or do SCNVectors just not allow you to use extensions?
"Value of type 'SCNVector4' has no member 'truncate'"
camRotation is a SCNVector4.
Your extension is on Double, not SCNVector4.
/// Double, not SCNVector4
extension Double {
func truncate(places : Int)-> Double {
return Double(floor(pow(10.0, Double(places)) * self)/pow(10.0, Double(places)))
}
}
Extensions work on any type, but camRotation.truncate(palces:2) doesn't work, because the type doesn't match.
Ok, now on to the actual answer. Currently, your code says
let CamPosition = SCNVector3(transform.m41, transform.m42, transform.m43)
Just apply truncate to each of the components:
let CamPosition = SCNVector3(
transform.m41.truncate(places: 2),
transform.m42.truncate(places: 2),
transform.m43.truncate(places: 2)
)
print(CamPosition)
Because these components take in Float values, you should also change your extension to this:
extension Float {
func truncate(places: Int) -> Float {
return Float(floor(pow(10.0, Float(places)) * self)/pow(10.0, Float(places)))
}
}
I've been having some fun using GameplayKit in a Scenekit (and ARKit, although that isn't important to this question) application.
Specifically, I have been using Entities, Components, and Agents for Behavior, and it has all been working great, except for one thing.
I have managed to use an GKAgent inside a component to move around a scene, and to avoid other objects. That all seems to be working. However, I can only get the GKAgent position working, not the "rotation" property.
import Foundation
import SceneKit
import GameplayKit
class MoveComponent: GKScnComponent, GKAgentDelegate {
//this class is abbreviated for perfunctory sakes
func agentWillUpdate(_ agent: GKAgent) {
guard let visualComponent = entity?.component(ofType: self.visualType) as? VisualComponent else {
return
}
//works with just "position", but turns possessed with both rotation and position
agentSeeking.rotation = convertToFloat3x3(float4x4: visualComponent.parentMostNode.simdTransform)
agentSeeking.position = visualComponent.parentMostNode.simdPosition
}
func agentDidUpdate(_ agent: GKAgent) {
guard let visualComponent = entity?.component(ofType: self.visualType) as? VisualComponent else {
return
}
//works with just "position", but turns possessed with both rotation and position
visualComponent.parentMostNode.simdPosition = agentSeeking.position
visualComponent.parentMostNode.simdTransform = convertToFloat4x4(float3x3: agentSeeking.rotation)
}
}
I have written some conversion code between 3x3 matrices and 4x4 matrices, thanks to this question:
Converting matrix_float3x3 rotation to SceneKit
import Foundation
import GameplayKit
func convertToFloat3x3(float4x4: simd_float4x4) -> simd_float3x3 {
let column0 = convertToFloat3 ( float4: float4x4.columns.0 )
let column1 = convertToFloat3 ( float4: float4x4.columns.1 )
let column2 = convertToFloat3 ( float4: float4x4.columns.2 )
return simd_float3x3.init(column0, column1, column2)
}
func convertToFloat3(float4: simd_float4) -> simd_float3 {
return simd_float3.init(float4.x, float4.y, float4.z)
}
func convertToFloat4x4(float3x3: simd_float3x3) -> simd_float4x4 {
let column0 = convertToFloat4 ( float3: float3x3.columns.0 )
let column1 = convertToFloat4 ( float3: float3x3.columns.1 )
let column2 = convertToFloat4 ( float3: float3x3.columns.2 )
let identity3 = simd_float4.init(x: 0, y: 0, z: 0, w: 1)
return simd_float4x4.init(column0, column1, column2, identity3)
}
func convertToFloat4(float3: simd_float3) -> simd_float4 {
return simd_float4.init(float3.x, float3.y, float3.z, 0)
}
I'm a little new to all this, and I'm not a linear algebra guru, so I'm not 100% certain if my matrix conversion functions are doing exactly what they are supposed to do.
When I just use the "position" property of the agent, everything is fine, but when I add in rotation/transform from the agent to the node, everything starts acting possessed.
Any thoughts/pointers/help with what I'm doing wrong?
I figured it out. Matrix multiplication is the key to getting this working. Anyone with experience with 3d game development probably could have told me that without any mental exertion :)
func agentWillUpdate(_ agent: GKAgent) {
guard let visualComponent = entity?.component(ofType: self.visualType) as? VisualComponent else {
return
}
agentSeeking.position = visualComponent.parentMostNode.simdPosition
let rotation = visualComponent.parentMostNode.rotation
let rotationMatrix = SCNMatrix4MakeRotation(rotation.w, rotation.x, rotation.y, rotation.z)
let float4x4 = SCNMatrix4ToMat4(rotationMatrix)
agentSeeking.rotation = convertToFloat3x3(float4x4: float4x4)
}
func agentDidUpdate(_ agent: GKAgent) {
guard let visualComponent = entity?.component(ofType: self.visualType) as? VisualComponent else {
return
}
// visualComponent.parentMostNode.simdPosition = agentSeeking.position
let rotation3x3 = agentSeeking.rotation
let rotation4x4 = convertToFloat4x4(float3x3: rotation3x3)
let rotationMatrix = SCNMatrix4FromMat4(rotation4x4)
let position = agentSeeking.position
let translationMatrix = SCNMatrix4MakeTranslation(position.x, position.y, position.z)
let transformMatrix = SCNMatrix4Mult(rotationMatrix, translationMatrix)
visualComponent.parentMostNode.transform = transformMatrix
}
I hope somebody finds this useful.
For converting from an agent's rotation 3x3 matrix to a scene node rotation, one convenient way is to use simd_quatf, so you can do something like:
node.simdWorldOrientation = simd_quatf(agent.rotation)
Similarly, using simd_float3x3 lets one convert from a quaternion (e.g. simdWorldOrientation) to a 3x3 rotation matrix. For example:
agent.rotation = simd_float3x3(node.simdWorldOrientation)
These let you avoid the extra conversion functions.
I have an array of array of coordinates (from a shapefile), which I'm trying to make into a series of SKSpriteNodes.
My problem is that I need to keep the relative positions of each of the shapes in the array. If I use SKShapeNodes, it works, as they are just created directly from the path I trace, but their resources consumption is quite high, and, in particular I cannot use lighting effects on them.
If I use SKSpriteNodes with a texture created from the shape node, then I lose their relative positions.
I tried calculating the center of each shapes, but their positions are still not accurate.
Here's how I draw them so far:
override func didMoveToView(view: SKView)
{
self.backgroundColor = SKColor.blackColor()
let shapeCoordinates:[[(CGFloat, CGFloat)]] = [[(900.66563867095, 401.330302084953), (880.569690215615, 400.455067051099), (879.599839322167, 408.266821560754), (878.358968429675, 418.182833936104), (899.37522863267, 418.54861454054), (900.66563867095, 401.330302084953)],
[(879.599839322167, 408.266821560754), (869.991637153925, 408.122907880045), (870.320569111933, 400.161243286459), (868.569953361733, 400.11339198742), (864.517810669155, 399.54973007215), (858.682258706015, 397.619367903278), (855.665753299048, 395.808813873244), (853.479452218432, 392.811211835046), (847.923492419877, 394.273974470316), (834.320860167515, 397.859104108813), (826.495867917475, 399.921507079808), (829.86572598778, 404.531781837208), (835.898936154083, 409.178035013947), (840.887737516875, 411.839958392806), (847.191868005112, 414.441797809335), (854.251943938193, 416.198384209245), (860.095769038325, 417.277496957155), (866.21091316512, 417.954970608037), (873.27118845149, 418.182833936104), (878.358968429675, 418.182833936104), (879.599839322167, 408.266821560754)],
[(931.018881691707, 402.151689416542), (910.610746904717, 401.600140235583), (910.380693886848, 411.576056681467), (930.79710181083, 411.750012342223), (931.018881691707, 402.151689416542)],
[(880.569690215615, 400.455067051099), (870.320569111933, 400.161243286459), (869.991637153925, 408.122907880045), (879.599839322167, 408.266821560754), (880.569690215615, 400.455067051099)]]
for shapeCoord in shapeCoordinates
{
let path = CGPathCreateMutable()
var center:(CGFloat, CGFloat) = (0, 0)
for i in 0...(shapeCoord.count - 1)
{
let x = shapeCoord[i].0
let y = shapeCoord[i].1
center.0 += x
center.1 += y
if i == 0
{
CGPathMoveToPoint(path, nil, x, y)
}
else
{
CGPathAddLineToPoint(path, nil, x, y)
}
}
center.0 /= CGFloat(shapeCoord.count)
center.1 /= CGFloat(shapeCoord.count)
let shape = SKShapeNode(path: path)
let texture = self.view?.textureFromNode(shape)
let sprite = SKSpriteNode(texture: texture)
sprite.position = CGPointMake(center.0, center.1)
//self.addChild(shape)
self.addChild(sprite)
}
}
Is it feasible or should I switch to another technology / method?
So, I'm trying to develop a simple game written in Swift, but I'm having trouble doing a pretty simple thing. I can't manage to create a random CGPoint... When using arc4random, a compiler error shows up telling me that I can't use Int32 in a CGPoint. So, Is there any way to do this? Any workaround? Thanks!
can also maybe make use of Swift's extensions of base types to create a reusable set of overloaded functions of CGPoint. Maybe something like:
extension CGPoint {
func random()->CGPoint { return CGPoint(x:Int(arc4random()%1000),y:Int(arc4random()%1000))}
func random(range:Int)->CGPoint {
return CGPoint(x:Int(arc4random()%range),y:Int(arc4random()%range))}
func random(rangeX:Int, rangeY:Int)->CGPoint {
return CGPoint(x:Int(arc4random()%rangeX),y:Int(arc4random()%rangeY))}
}
You can then write random CGPoints like this:
var p = CGPoint.random()
//random x and y with a range of 1000
or
var p = CGPoint.random(range:100)
//random x and y with a range of 100
or
var p = CGPoint.random(rangeX:200, rangeY:400)
//random x up to 200 and random y with a range of up to 400
Granted, I'm not in the Xcode IDE at the moment to check syntax / if it compiles correctly but hope that could be of help :-)
...
//////////////////
Swift 1.2 Update
//////////////////
Seems these type-level function calls are not allowed anymore with extensions...at least for CGPoint; probably because CGPoint is actually a struct and not a class based on the current IOS documentation.
Here's a more in-depth version of my extension that allows for Range types.
This is confirmed working as of XCode 6.4 Beta
(Github repository with Playground file found here:
https://github.com/princetrunks/Random-CGPoint-Extension)
//creates random CGPoints in Swift as of XCode Beta 6.4 (6E7)
extension CGPoint {
/*private functions that help alleviate the ambiguity of the modulo bias
and nested typecasting as well as recycle similar functionality
for either Int or Range type parameter inputs */
private func randomInt(num:Int) ->Int{
return Int(arc4random_uniform(UInt32(num)))
}
private func randomIntFromRange(numRange:Range<Int>) ->Int{
return Int(arc4random_uniform(UInt32((numRange.endIndex - numRange.startIndex) + numRange.startIndex)))
}
//private variable for the default range
private var defaultRange : Int{
get{return 1000}
}
//(a) public variable that creates a default random CGPoint
static var randomPoint = CGPoint.zeroPoint.random()
//(b) default random point creation
func random()->CGPoint { return CGPoint(x:randomInt(defaultRange),y:randomInt(defaultRange))}
//(c) using an Int parameter for both the random x and y range
func random(range:Int)->CGPoint {
return CGPoint(x:randomInt(range),y:randomInt(range))
}
//(d) allows for the specification of the x and y random range
func random(#rangeX:Int, rangeY:Int)->CGPoint {
return CGPoint(x:randomInt(rangeX),y:randomInt(rangeY))
}
//(e) allows the same functionality as (c) but with a Range<Int> type parameter
func random(range:Range<Int>)->CGPoint {
return CGPoint(x:randomIntFromRange(range), y:randomIntFromRange(range))
}
//(f) allows the same functionality as (d) but with a Range<Int> type parameter
func random(#rangeX:Range<Int>, rangeY:Range<Int> )->CGPoint {
return CGPoint(x:randomIntFromRange(rangeX), y:randomIntFromRange(rangeY))
}
}
Here's how we can test this extension:
//(a)
let r = CGPoint.randomPoint
//(b)
var anotherRandomPoint = r.random()
//(c)
anotherRandomPoint = r.random(1000)
//(d)
anotherRandomPoint = r.random(0...1000)
//(e)
anotherRandomPoint = r.random(rangeX:90, rangeY: 2000)
//(f)
anotherRandomPoint = r.random(rangeX:0...90, rangeY: 0...2000)
// generates 100 random CGPoints between -1000 and 999
for _ in 0...100 {
anotherRandomPoint.random(-1000...1000)
}
hi what about constructing an Int? Int(arc4random())
e.g.
var p = CGPoint(x:Int(arc4random()%1000),y:Int(arc4random()%1000))
Swift 4,5
// Add some range
let minX = 0
let maxX = 100
let minY = 0
let maxY = 100
let randomX = CGFloat.random(in: minX..<maxX)
let randomY = CGFloat.random(in: minY..<maxY)
let random = CGPoint(x: randomX, y: randomY)
Here is an extension on CGPoint to generate random point based on your x,y closed range.
extension CGPoint {
static func randPoint(xRange: ClosedRange<CGFloat>, yRange: ClosedRange<CGFloat>) -> Self {
let x = CGFloat.random(in: xRange)
let y = CGFloat.random(in: yRange)
return .init(x: x, y: y)
}
}