In an earlier post I asked about the right settings for initializing a SCNParticleEngine. With some hints from Zay
I managed to get both the SCNP and code paths to work – almost identically. Except that the SCNP path had the particles shrinking over time while the coding path did not.
Turns out that buried inside the SCNP file is a dictionary entry “Size” which is a SCNParticlePropertyController. Here’s the code from the debugger:
The gotcha is that when I open the SCNP file in the editor, I can see the stars being animated etc. But there is no sign of an Action or any affordance to open such an editor. Now, this may be related to the fact that the SCNP file is all about the PE parameters for initializing the PE. There is NOT an actual node or object on the stage. One thought is you need to have an editable action or Xcode won’t open the Action editor?
How this action got there or how I might edit it is not clear. I assume someone put it in the SCNP in an earlier version of Xcode. Then the Action was forgotten. Or something like that. It may simply be a bug/corrupted SCNP file.
As you mention this is Core Animation animation driving SCNParticlePropertySize. It is possible to configure it in the SceneKit scene editor via the Attributes inspector (not the Action editor).
To be able to control the size of your particles over time (i.ex. let them shrink) using code only, you have to use a SCNPropertyController and a CAKeyframeAnimation like so:
// ANIMATE PARTICLE SIZE OVER TIME
let animationSize = CAKeyframeAnimation()
animationSize.values = [0.7, 0.3, 0.2, 0.0]
animationSize.keyTimes = [0.0, 0.125, 0.3, 1.0]
animationSize.duration = CFTimeInterval(10.0)
animationSize.calculationMode = .cubic
animationSize.timingFunction = CAMediaTimingFunction.init(name: .linear)
and then you do:
// SET PROPERTY CONTROLLER
let sizeController = SCNParticlePropertyController(animation: animationSize)
myParticleSystem.propertyControllers = [SCNParticleSystem.ParticleProperty.size: sizeController]
with the same approach you can animate the particle colours in a changing sequence over time.
Related
My app is dynamically creating sublayers ("myLayer1" below). I am using the following code to set it, which works well
view.layer.insertSublayer(myLayer1, at: UInt32(sublayersCount))
Obviously each time it runs, there is a new myLayer1, and the old myLayer1 is lost
I want to store these OLD myLayer1's, and cycle through them with an IBACTION ("back" button)
I tried something like the following, to give me 5 possibilities
myLayer5 = myLayer4
myLayer4 = myLayer3
myLayer3 = myLayer2
myLayer2 = myLayer1
As a test, I dynamically created a few layers, and then ran this via my IBACTION:
view.layer.insertSublayer(myLayer2, at: UInt32(sublayersCount))
However, it did not display the past layer
What is the best way to achieve this?
1- If you do
view.layer.insertSublayer(myLayer2, at: UInt32(sublayersCount))
Then it's equivalent to
view.layer.addSublayer(myLayer2)
2-
The added layer hides the previous because you may need to set
myLayer2.opacity = // 0.0 --- 1.0
3-
Instead of creating vars use
view.layer.sublayers
to access all current sublayers
I'm looking for a solution for my problem:
I have defined a animation as SCNAction with the action editor in Xcode. Now I want to control it with a slider. So basically manipulating the slider shows a specific time/frame in the animation.
Right now, I can just pause/play the animation or reset it to the beginning. I achieved this with Swift code addressing the SCNAction from the action editor.
As far as I researched, there is no possibility
to animate a SCNAction to a specific time immediately and stop right there. That would work.
I'm also interested in other attempts if this is not achievable with SCNActions. It may be that this is not realizable at all. Thank you.
I found a solution: I loaded a CAAnimation from a .dae file(For this look here:https://stackoverflow.com/a/24924702/9625548, bit outdated but still usable).
With the SCNAnimationPlayer I can address the CAAnimation with a key. With the following
code I can control my animation with a UISlider:
let moment: TimeInterval = TimeInterval(self.animationTimeSlider.value)
guard let animationPlayer: SCNAnimationPlayer = node.animationPlayer(forKey: key) else{
return
}
animationPlayer.animation.timeOffset = moment
animationPlayer.play()
animationPlayer.speed = 0.0
Basically, I read the thumb and convert it to TimerInterval. I'm looking for the SCNAnimationPlayer and aborting if no SCNAnimationPlayer is found for the given key. Then I set the .timeOffset to the moment I want to display. With play(), the animation starts from the beginning + .timeOffset. Finally, set the .speed to zero. the animation is not running but shows the moment.
Attention: If you let your animation running and also manipulating it in that way, you have to reset it. Otherwise it is getting messed up. You can catch that case with a boolean that saves if you let your animation run normally. Then you know you have to reset it before manipulating with the slider. For example like this:
node.removeAllAnimations()
node.addAnimation(animation, forKey: key)
self.sceneView.scene.rootNode.animationPlayer(forKey: key)?.paused = true
I've created a particle system in the Scene Editor (not the particle Editor), and it's named "particles" (this is the default name).
Back in the ViewController, I'm attempting to get a reference to this particle system and change some properties of it.
But I can't figure out why this doesn't work:
let particleSystem = SCNParticleSystem(named: "particles", inDirectory: "")
particleSystem?.isAffectedByGravity = true
I know it's possible to set gravity on within the Scene Editor, but I'm simply using this as a test to see if the reference to the Particle System is working. It's not.
What am I missing or doing wrong?
ADDITIONAL EFFORTS:
As per Rickster's suggestion, trying this:
let particleSystems = scene.particleSystems
let myParticleSystem = particleSystems?[0]
myParticleSystem?.isAffectedByGravity = true
print(particleSystems)
This has now this problem:
My thinking (faulty as it is) was that the array's 0 location would have the only particle system I have in this scene.
Here is a picture of the scene I got:
And the code to get a reference to the particle system assigned to the particles SCNNode instance:
In Swift 2.3 (and earlier)
let particlesNode:SCNNode = scene.rootNode.childNodeWithName("particles", recursively: true)!
let particleSystem:SCNParticleSystem = (particlesNode.particleSystems?.first)!
In Swift 3.0
let particlesNode:SCNNode = scene.rootNode.childNode(withName: "particles", recursively: true)!
let particleSystem:SCNParticleSystem = (particlesNode.particleSystems?.first)!
Alternative Array Index Syntax
let particleSystem:SCNParticleSystem = (particlesNode.particleSystems?[0])!
As the docs for that initializer suggest, it's for loading particle systems that are in their own files. When you make a particle system in the scene editor, the particle system isn't its own file — it's part of the scene file you're making.
To find it... well, first you have to load the SCNScene object that file represents. Assuming you've done that already, you'll need one of two ways to access the particle system depending on how you defined it in the editor:
If the particle system is attached to a node — the straightforward way to position it in the scene — you can use the scene's rootNode property to get at the scene contents, then use methods like this one to find the node containing the particle system using the name you assigned it in the editor. (You did give it a name in the editor, right? If not, go do that... or worst case, there's always this method that lets you search for nodes based on any test you can code.)
If not, use the scene's particleSystems property to find all systems that aren't attached to nodes.
I am trying to persist markers in an augmented reality game. Here is the gist of what I am doing:
I have my users recording and saving an area to an ADF. Then they drop marker’s into the scene and save out their position data in Unity World coordinates to a text file. I then restart the app, load and localize to the ADF and load the markers.
In order to get this working, I've modified the ARPoseController.cs file in the Unity demo package to use the Area Description as it's base frame. In the _UpdateTransformation method I've swapped out the frame pairs
pair.baseFrame = TangoEnums.TangoCoordinateFrameType.TANGO_COORDINATE_FRAME_START_OF_SERVICE;
pair.targetFrame = TangoEnums.TangoCoordinateFrameType.TANGO_COORDINATE_FRAME_DEVICE;
for
pair.baseFrame = TangoEnums.TangoCoordinateFrameType.TANGO_COORDINATE_FRAME_AREA_DESCRIPTION;
pair.targetFrame = TangoEnums.TangoCoordinateFrameType.TANGO_COORDINATE_FRAME_DEVICE;
I've also added some code confirming that I'm successfully localizing to the ADF, but I'm noticing that my markers position in Unity World Space do not position properly relative to real environment.
I can confirm that my markers save and load properly based on START_OF_SERVICE origin so I assume that they are properly serializing and deserializing. What could be causing this? Am I wrong in assuming this should just work by switching the base framepair to Area_Description instead of START_OF_SERVICE?
I had a similar problem getting the AR and ADF integrated, I had to modify the TangoPointCloud to check if you're using an AreaDescription in OnTangoDepthAvailable() and adjust the baseFrame target as required.
i.e.:
if (m_tangoDeltaPoseController.m_useAreaDescriptionPose)
{
pair.baseFrame = TangoEnums.TangoCoordinateFrameType.TANGO_COORDINATE_FRAME_AREA_DESCRIPTION;
pair.targetFrame = TangoEnums.TangoCoordinateFrameType.TANGO_COORDINATE_FRAME_DEVICE;
}
else
{
pair.baseFrame = TangoEnums.TangoCoordinateFrameType.TANGO_COORDINATE_FRAME_START_OF_SERVICE;
pair.targetFrame = TangoEnums.TangoCoordinateFrameType.TANGO_COORDINATE_FRAME_DEVICE;
}
That way, the geometry of the point cloud adjusts itself based on the ADF offset instead of from device start.
After that change, when I'm using the sample code for AR to drop markers, it registers the surface properly so I'm placing the markers in the correct spots and orientation. I'm still encountering some flakiness with the markers not adjusting when relocalized though, have to look into the AreaLearningInGameController for loop closure events.
Hope that helps!
I have the function below to show an emitter. It is supposed to pick which asks file to use for the emitter based upon the color passed into the function. The sks files have been created and named based upon their colors and they display the proper coloring in Xcode.
However when run on the simulator or the device, it does not appear that the coloring from the sks file is honored. No matter what color is passed in, the emitter shows the same particle colors. BTW this is a spark based emitter.
Any ideas what I may be doing wrong?
func showEmitter(theColor:String){
var ourEmitterName:String?
switch(theColor) {
case "black","white":
ourEmitterName = "blackwhiteemitter"
default:
ourEmitterName = "\(theColor)emitter"
}
let emitterPath = NSBundle.mainBundle().pathForResource(ourEmitterName, ofType: "sks")
let thisEmitter:SKEmitterNode = NSKeyedUnarchiver.unarchiveObjectWithFile(emitterPath!) as SKEmitterNode
thisEmitter.zPosition = SceneLevel.background.rawValue
self.addChild(thisEmitter)
}
Thanks for your help - Ken
I found the problem. In my emitters I had the Blend Mode set to 'Add'. After I changed it to 'Alpha' everything worked fine.
Not sure why that solved it, since I don't know to what was 'added' to what.