In SceneKit we can not create PhysicalBody at every location, is that normal? - ios

At sceneView we have a big cube.
SCNBox(width: 10, height:10, length: 10, chamferRadius: 0)
We are placing spheres(radius = 1) on the six surfaces of that bigger cube.
Spheres have dynamic physicalBody.
Debugger mode is active with
sceneView.debugOptions = [.showPhysicsFields, .showPhysicsShapes]
When we run app we can see that only sphere with node position
SCNVector3(x:0, y: 0, z: 10.5)
seems to have body, only this one marked by debugger.
Also, only this sphere responds if a kinetic body touches it.
Is there something we are missing, why the spheres on other surfaces can not have physical body.
This is how I create spheres;
var ball:[SCNSphere] = []
var ballNode:[SCNSNode] = []
var body:[SCNPhysicsBody] = []
let pShape = SCNSphere()
pShape.radius = 1
pShape.segmentCount = 1
for k in 0..<6 {
ball.append(SCNSphere())
ballNode.append(SCNSphere())
ball[k].radius = 1
ball[k].hunt.firstMaterial?.diffuse.contents = UIColor.red
ballNode[k]= SCNNode(geometry:ball[k])
let shape = SCNPhysicsShape(geometry: pShape, options: nil)
body.append(SCNPhysicsBody())
body[k] = SCNPhysicsBody(type: .dynamic, shape: shape)
body[k].isAffectedByGravity = false
body[k].friction = 1
body[k].restitution = 0.1
body[k].angularDamping = 1
ballNode[k].physicsBody = body[k]
let coors = createCoors(index: k)
ballNode.position = SCNVector3(coors.x, coors.y, coors.z)
bigBoxNode.addChildNode(ballNode[k])
}

Related

Swiftchars - bars chart overlap after specific number of points

How can I specify min space between bars in bar chart using SwiftChars library ?
Here is my code sample for using SwiftCharts to display bars chart:
// xAxis
let labelSettings = ChartLabelSettings(font: .systemFont(ofSize: 13.0), fontColor: .aiduBlue)
var xValues = [ChartAxisValue]()
xValues.append(ChartAxisValueString(order: -1))
for (index, point) in barsDataSource.enumerated() {
let dateString = DateFormatter.shortDayOfWeekDayOnTwoLinesFormatter.string(from: point.departureDate)
xValues.append(ChartAxisValueString(dateString, order: index, labelSettings: labelSettings))
}
xValues.append(ChartAxisValueString(order: xValues.count - 1))
let xModel = ChartAxisModel(axisValues: xValues,
lineColor: .aiduSkyBlue,
axisTitleLabel: ChartAxisLabel(text: "", settings: labelSettings))
// yAxis
let yAxisMinSpaceBetweenPoints: CGFloat = 10.0
let yAxisTopBottomMargin = (((barsMaxPrice - barsMinPrice) / Double(barsDataSource.count)) + Double(yAxisMinSpaceBetweenPoints)) + 10
let yAxisGenerator = ChartAxisValuesGeneratorNice(minValue: barsMinPrice - yAxisTopBottomMargin,
maxValue: barsMaxPrice + yAxisTopBottomMargin,
preferredDividers: 1,
minSpace: yAxisMinSpaceBetweenPoints,
maxTextSize: CGFloat.greatestFiniteMagnitude,
multiplierUpdateMode: .nice)
let yLabelsGenerator = ChartAxisLabelsGeneratorFunc {scalar in
return ChartAxisLabel(text: "", settings: ChartLabelSettings())
}
let yModel = ChartAxisModel(lineColor: UIColor.white.withAlphaComponent(0),
firstModelValue: barsMinPrice,
lastModelValue: barsMaxPrice,
axisTitleLabels: [],
axisValuesGenerator: yAxisGenerator,
labelsGenerator: yLabelsGenerator)
// Char Bars layer
let frame = chartFrame(containerView.bounds)
let coordsSpace = ChartCoordsSpaceLeftBottomSingleAxis(chartSettings: chartSettings,
chartFrame: frame,
xModel: xModel,
yModel: yModel)
let (xAxisLayer, yAxisLayer, innerFrame) = (coordsSpace.xAxisLayer, coordsSpace.yAxisLayer, coordsSpace.chartInnerFrame)
let barsModels: [ChartBarModel] = barsDataSource.enumerated().flatMap { index, item in
[
ChartBarModel(constant: ChartAxisValueInt(index),
axisValue1: ChartAxisValueDouble(0),
axisValue2: ChartAxisValueDouble(item.price),
bgColor: barColor(price: item.price, minPrice: barsMinPrice))
]
}
let chartBarSettings = ChartBarViewSettings(animDuration: 0,
animDelay: 0,
cornerRadius: 0,
roundedCorners: .allCorners,
selectionViewUpdater: nil,
delayInit: false)
let chartBarsLayer = ChartBarsLayer(xAxis: xAxisLayer.axis,
yAxis: yAxisLayer.axis,
bars: barsModels,
horizontal: false,
barWidth: 40,
settings: chartBarSettings,
mode: .translate,
tapHandler: { [weak self] (tappedBar) in
self?.chartBarTapHandler(tappedBar: tappedBar)
}, viewGenerator: { [weak self] (p1, p2, barWidth, color, settings, model, index) -> ChartPointViewBar in
var barBGColor = color
if let s = self, let lastTappedBarModel = s.lastTappedBarModel {
let currentBarModel = s.dataSource[index]
barBGColor = currentBarModel.departureDate == lastTappedBarModel.departureDate && currentBarModel.duration == lastTappedBarModel.duration ? s.barSelectionBgColor : color
}
let view = ChartPointViewBar(p1: p1, p2: p2, width: barWidth, bgColor: barBGColor, settings: settings)
return view
})
// Price labels layer
let labelToBarSpace: Double = 20
let labelChartPoints = barsModels.map { bar in
ChartPoint(x: bar.constant, y: bar.axisValue2.copy(bar.axisValue2.scalar + labelToBarSpace))
}
let priceLabelsLayer = ChartPointsViewsLayer(xAxis: xAxisLayer.axis,
yAxis: yAxisLayer.axis,
chartPoints: labelChartPoints,
viewGenerator: {(chartPointModel, layer, chart) -> UIView? in
let label = HandlingLabel()
label.text = PriceFormatter.string(fromPrice: Float(chartPointModel.chartPoint.y.scalar - labelToBarSpace))
label.font = .boldSystemFont(ofSize: 10.0)
label.textColor = UIColor.aiduBlue
label.sizeToFit()
let pos = chartPointModel.chartPoint.y.scalar > 0
label.center = CGPoint(x: chartPointModel.screenLoc.x, y: pos ? innerFrame.origin.y : innerFrame.origin.y + innerFrame.size.height)
label.alpha = 0
label.isUserInteractionEnabled = false
label.movedToSuperViewHandler = {[weak label] in
label?.alpha = 1
label?.center.y = chartPointModel.screenLoc.y
}
return label
}, displayDelay: 0, mode: .translate)
return Chart(
frame: frame,
innerFrame: innerFrame,
settings: chartSettings,
layers: [
xAxisLayer,
yAxisLayer,
chartBarsLayer,
priceLabelsLayer
]
)
private var chartSettings: ChartSettings {
var chartSettings = ChartSettings()
chartSettings.leading = 2
chartSettings.top = 2
chartSettings.trailing = 2
chartSettings.bottom = 2
chartSettings.axisStrokeWidth = 0.4
chartSettings.spacingBetweenAxesX = 2
chartSettings.spacingBetweenAxesY = 2
chartSettings.labelsSpacing = 10
chartSettings.labelsToAxisSpacingY = 0
chartSettings.spacingBetweenAxesY = 0
chartSettings.axisTitleLabelsToLabelsSpacing = 0
chartSettings.zoomPan.panEnabled = true
chartSettings.zoomPan.zoomEnabled = false
chartSettings.zoomPan.maxZoomX = 3
chartSettings.zoomPan.minZoomX = 3
chartSettings.zoomPan.minZoomY = 1
chartSettings.zoomPan.maxZoomY = 1
return chartSettings
}
When data source contains around (10 for ex) points, chart working without overlapping, but without fixed space between bars if you changed (increase/decrease) data source:
When data source contains around (35 for ex) points, bars overlapped:
SwiftCharts version 0.6.5
Swift version 5
Finally I found solution:
I had to set the minZoomX and maxZoomX (in chart settings) with fixed value which the number of pages for the chart (or number of scrollable area than the original width).
why x ? because I want chart to scroll horizontally.
here is code:
let barWidth: CGFloat = 30.0
let spaceBetweenBars: CGFloat = 80.0
let zoomXValue = CGFloat(barsDataSource.count) / (UIScreen.main.bounds.width / (barWidth + spaceBetweenBars))
chartSettings.zoomPan.minZoomX = zoomXValue
chartSettings.zoomPan.maxZoomX = zoomXValue

SceneKit Child Physicsbody not moving when parent node is moving

I have 2 simple nodes (2 balls : one above the other and not in contact) each with a physicsbody with the same characteristics. The one below is the parent node, and the one above is a child node.
When I apply a force to the parent node, both nodes are moving but the physicsbody of the child node does not move. I have activated showphysicsshape option, and I see the physicsbody of the child staying in the original place.
Am I missing something ?
Class Character3DClass: CharacterClass
{
var CharacterNode: SCNNode!
var CenterBody: SCNNode!
func InitChar()
{
InitBodyParentNode()
AddCenterBody()
}
func InitBodyParentNode()
{
CharacterNode.physicsBody = SCNPhysicsBody(type: .dynamic, shape: SCNPhysicsShape(node: CharacterNode, options: [:]))
CharacterNode.physicsBody?.isAffectedByGravity = false
CharacterNode.physicsBody?.mass = 1
CharacterNode.physicsBody?.charge = 0
CharacterNode.physicsBody?.friction = 0
CharacterNode.physicsBody?.rollingFriction = 0
CharacterNode.physicsBody?.restitution = 0
CharacterNode.physicsBody?.damping = 0
CharacterNode.physicsBody?.angularDamping = 0
// Affectation des caractéristiques pour la gestion des collisions
CharacterNode.physicsBody?.categoryBitMask = eNodePhysics.Player.rawValue
CharacterNode.physicsBody?.contactTestBitMask = eNodePhysics.Player.rawValue | eNodePhysics.Ennemy.rawValue
CharacterNode.physicsBody?.collisionBitMask = eNodePhysics.TileBorder.rawValue
}
func AddCenterBody()
{
CenterBody = SCNNode(geometry: Level3D.Dot.geometry)
CenterBody.categoryBitMask = eNodePhysics.PlayerCenter.rawValue
CenterBody.position = SCNVector3.init(0, 3, 0)
CenterBody.scale = SCNVector3.init(0.4, 0.4, 0.4)
CenterBody.physicsBody = SCNPhysicsBody(type: .dynamic, shape: SCNPhysicsShape(geometry: CenterBody.geometry!, options: nil))
CenterBody.physicsBody?.isAffectedByGravity = false
CenterBody.physicsBody?.mass = 1
CenterBody.physicsBody?.charge = 0
CenterBody.physicsBody?.friction = 0
CenterBody.physicsBody?.rollingFriction = 0
CenterBody.physicsBody?.restitution = 0
CenterBody.physicsBody?.damping = 0
CenterBody.physicsBody?.angularDamping = 0
CenterBody.physicsBody?.categoryBitMask = eNodePhysics.PlayerCenter.rawValue
CenterBody.physicsBody?.contactTestBitMask = eNodePhysics.PlayerCenter.rawValue
CenterBody.physicsBody?.collisionBitMask = eNodePhysics.NoValue.rawValue
CharacterNode.addChildNode(CenterBody)
}
}
You can use the func resetTransform() method to make the physics body match it's node, but it will cost some performance : https://developer.apple.com/documentation/scenekit/scnphysicsbody/1514782-resettransform

"lldb" crash with XCODE & ARKIT : 0 __ UpdateAudioTransform

I have written a game using ARKIT that pops up random nodes (with drones) in the 3d space around the player, using ARKIT, and I get an incomprehensible crash, as only thing in the console is "lldb" , and the rest of the crash details are on the screenshot attached (I am still a newbie in Swift so not able to debug it).
The crash happens when there's a lot of nodes on the screen (maybe 20-30) and the FPS drops - ie happens "mid - game" and the FPS drops a lot.
Can someone point me to the right direction for this crash?
The part of the code that is in my opinion relevant is the function that spawns the random 3d nodes (they also have SCNActions attached that play sounds when they're tapped - perhaps this could be relevant as the left hand side debugger opens with that line highlighted when the crash occurs, as per the photo attached). In case it is also relevant, the program uses some SCNParticleSystem calls as well. Attaching relevant code, and also the snapshot of the crash screen:
var droneSound : SCNAudioSource()
override func viewDidLoad() {
super.viewDidLoad()
droneSound = SCNAudioSource(named: "Sounds/drone1.wav")!
playDroneSound = SCNAction.playAudio(self.droneSound, waitForCompletion: true)
}
func startGame() {
DispatchQueue.main.async {
self.spawnTimer = Timer.scheduledTimer(timeInterval: TimeInterval(self.randomFloat(min: 2.5, max: 5)), target: self, selector: #selector(self.spawnDronesInterim), userInfo: nil, repeats: true)
}
}
#objc func spawnDronesInterim() {
for _ in 0...5 {
spawnDrone()
}
}
#objc func spawnDrone() {
let newDroneScene = SCNScene(named: "Ar.scnassets/DroneScene.scn")!
var newDroneNode = newDroneScene.rootNode.childNode(withName: "Drone", recursively: false)!
newDroneNode.name = "Drone\(self.droneCounter)"
newDroneNode = newDroneScene.rootNode.childNode(withName: "Drone\(self.droneCounter)", recursively: false)!
newDroneNode.position.x = newDroneNode.presentation.position.x + self.randomFloat(min: -10, max: 10)
newDroneNode.position.y = newDroneNode.presentation.position.y + self.randomFloat(min: -1, max: 5)
newDroneNode.position.z = newDroneNode.presentation.position.z + self.randomFloat(min: -10, max: 10)
let move1 = SCNAction.move(to: SCNVector3((self.randomFloat(min: -7, max: 7)), (self.randomFloat(min: 1, max: 5)), (self.randomFloat(min: -7, max: 7))), duration: 15)
let disappearMove = SCNAction.move(to: SCNVector3((self.randomFloat(min: -10, max: 10)), (self.randomFloat(min: 1, max: 5)), (self.randomFloat(min: -10, max: 10))), duration: 3)
let rotateAction = SCNAction.run { (SCNNode) in
let rotate = SCNAction.rotateBy(x: 0, y: CGFloat(360.degreesToRadians), z: 0, duration: 2)
newDroneNode.runAction(rotate)
}
let removeIt = SCNAction.removeFromParentNode()
let sequence = SCNAction.sequence([waitFive,move1,rotateAction,waitFive,disappearMove,waitTwo,removeIt])
newDroneNode.runAction(sequence)
self.sceneView.scene.rootNode.addChildNode(newDroneNode)
if self.droneCounter >= 5 {
self.sceneView.scene.rootNode.childNode(withName: "Drone\(self.droneCounter)", recursively: true)!.runAction(SCNAction.repeatForever(self.playDroneSound))
}
self.droneCounter += 1
}
when user taps on one of the 3d nodes, this gets called:
func handleExplosion (node : SCNNode) {
self.sceneView.scene.rootNode.childNode(withName: node.name!, recursively: true)!.runAction(self.playExplosionSound)
node.opacity = 0
print (self.sceneView.scene.rootNode.position)
let confetti = SCNParticleSystem(named: "Ar.scnassets/confetti.scnp", inDirectory: nil)
confetti?.loops = false
confetti?.particleLifeSpan = 1.5
confetti?.emitterShape = node.geometry
let confettiNode = SCNNode()
confettiNode.addParticleSystem(confetti!)
confettiNode.position = node.presentation.position
self.sceneView.scene.rootNode.addChildNode(confettiNode)
let fire = SCNParticleSystem(named: "Ar.scnassets/fire", inDirectory: nil)
fire?.loops = false
fire?.particleLifeSpan = 0.1
fire?.emitterShape = node.geometry
let fireNode = SCNNode()
fireNode.addParticleSystem(fire!)
fireNode.position = node.presentation.position
self.sceneView.scene.rootNode.addChildNode(fireNode)
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
node.removeFromParentNode()
self.killedLabel.text = "Killed: \(self.killedCount)"
self.dronesOut -= 1
}
}
Any pointer to the right direction for solving this crash would be greatly appreciated]1

How to lower Renderer Utilization?

I am having sever performance issues with a SceneKit game. Total vertices count 4000. Renderer Utilization is 100% on ipad3, framerate 16fps, even though Tiler utilization is only about 8%. This is screenshot from OPENGL ES Analysis.
This is the entire code that is used to specify what the object should look like. As you can see there is no custom shaders. Just a box that falls on the floor because of physics applied to it. Textures and 2 lights are applied in the .scn file. Everything is simple, basic but the performance is terrible. How do I lower Renderer Utilization on the device? I am guessing some default settings that sceneKit uses aren't suitable for my app and cause severe performance problems. Which one should I double check?
//called in viewDidLoad:
func setupScene(){
scene = SCNScene(named: "GameScene.scn")
sceneView.scene = scene
sceneView.delegate = self
scene.physicsWorld.contactDelegate = self
scene.physicsWorld.gravity = SCNVector3Make(0, 0, 0)
scene.physicsWorld.speed = 1.8
sceneView.jitteringEnabled = true
sceneView.autoenablesDefaultLighting = false
sceneView.antialiasingMode = SCNAntialiasingMode.None
let valueVector: NSValue = NSValue(SCNVector3: SCNVector3Make(0.7, 0.7, 0.7))
objectNode = scene.rootNode.childNodeWithName("object", recursively: true)!
let physicsShape = SCNPhysicsShape(node: objectNode, options: [SCNPhysicsShapeTypeKey:SCNPhysicsShapeTypeBoundingBox, SCNPhysicsShapeScaleKey:valueVector])
objectNode.physicsBody = SCNPhysicsBody(type: SCNPhysicsBodyType.Dynamic, shape: physicsShape)
let floorNode = scene.rootNode.childNodeWithName("floor", recursively: true)
floorNode?.physicsBody = SCNPhysicsBody(type: SCNPhysicsBodyType.Static, shape: nil)
objectNode.categoryBitMask = 1
floorNode?.categoryBitMask = 1
objectNode.physicsBody?.categoryBitMask = 1
floorNode?.physicsBody?.categoryBitMask = 1
objectNode.physicsBody?.collisionBitMask = 1
floorNode?.physicsBody?.collisionBitMask = 1
floorNode?.physicsBody?.restitution = 0.7
objectNode.physicsBody?.restitution = 0.7
}
func speed(velocity:SCNVector3) -> Float
{
let dx = Float(velocity.x)
let dy = Float(velocity.y)
let dz = Float(velocity.z)
return sqrtf(dx*dx+dy*dy+dz*dz)
}
func angularSpeed(angularSpeed: SCNVector4)->Float{
let x = angularSpeed.x
let y = angularSpeed.y
let z = angularSpeed.z
return sqrtf(x*x+y*y+z*z)
}
func nearlyAtRest(node:SCNNode) -> Bool
{
return speed((node.physicsBody?.velocity)!) < 0.05 && ( self.resting == false) && (angularSpeed((node.physicsBody?.angularVelocity)!) < 1)
}
// PHYSICS CONTACT DELEGATE DELEGATE METHOD
func renderer(renderer: SCNSceneRenderer, didSimulatePhysicsAtTime time: NSTimeInterval) {
if nearlyAtRest(objectNode) && speed((objectNode.physicsBody?.velocity)!) != 0 {
print("it stopped")
}

SceneKit rayTestWithSegmentFromPoint, hitResults always nil

Ok this one has had me scratching my head for the past couple of hours.
I'm attempting to do raytesting on an SCNScenePhysicsWorld (hitTest in SCNRenderer works without a glitch though)
Steps i take:
1.- properties:
let scene = SCNScene()
var sceneView = SCNView(frame: CGRectZero, options: [SCNPreferredRenderingAPIKey:NSNumber(unsignedLong: SCNRenderingAPI.OpenGLES2.rawValue)])
// hit sphere
let hitSphere = SCNSphere(radius: 55)
var hitNode : SCNNode! = nil
var hitShape : SCNPhysicsShape! = nil
var hitBody : SCNPhysicsBody! = nil
2.-
setupWorld {
sceneView.scene = scene
sceneView.delegate = self
.
.
.
hitNode = SCNNode(geometry: hitSphere)
hitNode.position = SCNVector3Make(0, 0, 0)
// neither work
hitShape = SCNPhysicsShape(geometry: hitSphere, options: nil)
// hitShape = SCNPhysicsShape(node: hitNode, options: nil)
hitBody = SCNPhysicsBody(type: .Static, shape: hitShape)
hitNode.physicsBody = hitBody
.
.
.
spheresNode.position = SCNVector3Make(0, 0, 0)
spheresNode.addChildNode(renderNode)
spheresNode.addChildNode(hitNode)
scene.delegate = self
}
3.-
func renderer(renderer: SCNSceneRenderer, didRenderScene scene: SCNScene, atTime time: NSTimeInterval) {
.
.
.
let rayHit = scene.physicsWorld.rayTestWithSegmentFromPoint(nearpoint, toPoint: farpoint, options: [SCNPhysicsTestBackfaceCullingKey:false]).first
i keep getting rayHit = nil but i'm 100% sure the hitBody lies within the nearpoint and farpoint
I more than aware i must be missing something here, not sure what though
any clues or pointers?
PS: the nearpoint is inside the hitSphere, the farpoint outside, i want to find the coordinate it hits at. I'm already doing it via SCNSceneRenderer.hitTest but want to learn how to do it by rayTest.
}

Resources