Restart SKEmitterNode without removing particles - swift

I have a particle effect for a muzzle flare set up. What I'm currently using is a low numParticlesToEmit to limit the emitter to a short burst, and doing resetSimulation() on the emitter whenever I want to start a new burst of particles.
The problem I'm having is that resetSimulation() removes all particles onscreen, and I often need to create a new burst of particles before the previous particles disappear normally so they get erased early.
Is there a clean way start up the emitter again without erasing the particles already onscreen?

Normally particle systems have a feature missing from SKEmitters: a duration. This controls how long a system emits. I don't see this in SKEmitter, despite being in SCNParticleSystems
Never mind, a work around:
SKEmitters have a numParticlesToEmit property and a particleBirthRate. Combined, these determine how long the particle system emits before shutting down.
Using these as a control of the emission it's possible to create pulses of particles emitted in the way you want for something like a muzzle flash or explosion.
I'm not sure if it remvoes itself when it reaches this limit. If not, you'll have to create a removal function of some sort. Because the way to get your desired effect (multiple muzzle flashes on screen) is to copy() the SKEmitter. This is quite efficient, so don't worry about overhead.

There is a targetNode on SKEmitters that are suppose to move the particles to another node so that when you reset the emitter, the previous particles still stay. Unfortunately, this is still bugged from what I can tell, unless somebody else has figured out how to get it working and I just missed it. Keep this in mind though in case they do ever fix it.

Hi to help future readers, the code that I use to calculate the duration of the emitter is this:
let duration = Double(emitter.numParticlesToEmit) / Double(emitter.particleBirthRate) + Double(emitter.particleLifetime + emitter.particleLifetimeRange/2)
It works perfectly for me
Extension:
extension SKEmitterNode {
var emitterDuration: Double {
return Double(numParticlesToEmit) / Double(particleBirthRate) + Double(particleLifetime + particleLifetimeRange/2)
}
}

Related

Best way to prevent physics calculations in Unity from eating too much FPS?

I'm a senior software engineer, but completely new to Unity programming. I've been teaching myself Unity for a few weeks by writing some SteamVR toys for myself. I wanted to ask about Unity's standard built-in ways to improve frame rate for a program with a lot of intensive physics calculation.
In my current VR scene, I have a ball with a Rigidbody on it. When I pick it up and throw it, I let it move naturally, but I apply small forces on every Update() call to adjust the landing position and always hit a target.
I saw my framerate take a big dive, so I wrote my own function to throttle the updates, limiting them to 5 per second. The question is, is there some standard Unity behavior that I should be using instead of rolling this code myself?
In my throwing script, I maintain a bunch of top level variables and update them first to decide whether I should do calculations on the current frame.
private void updateCalculationFrame() {
isCalculationFrame = Time.time > nextCalculationTime;
while (nextCalculationTime < Time.time) {
nextCalculationTime += (1 / calculationsPerSecond);
}
}
On each frame, I run this function, then if isCalculationFrame is true, I proceed to calculate and add force vectors. I just wonder if I am overlooking some more standard way to do this with less code? I feel like this must be a very common thing to try to do.
The code you have there will freeze the Unity until the loop is finished. You shouldn't do this at all.
You could simply set a new Time.fixedDeltaTime. By default it is usually about 0.02 but you can set it to anything you want.
The interval in seconds at which physics and other fixed frame rate updates (like MonoBehaviour's FixedUpdate) are performed.
...
Note that the fixedDeltaTime interval is with respect to the in-game time affected by timeScale.

Unity3D forcing object to a given orientation with minimum artificial effect

In my board game, the points are given by throwing 7 sea-shells cowry shell. These shells are dropped onto a sphere in Unity so they get rolled over randomly to different places. Once the rigidbody.isSleeping() returns true, I do a Raycast(from the belly side downwards) to figure out the orientation of the shell. If it is NOT a hit we know the shells belly is turned upside which means a point.
All is good and very realistic when in single player mode. Reason is I just activate the gravity of the shells and they dropped on to sphere, gets rolled randomly and when stopped i get the marks as stated above.
Now the problem is I am making the game multiplayer. In this case, I sent the randomly generated marks from the server and client will have to animate the shells to represent the marks. For example, if server send 3, out of 7 shells, 3 should have it's belly turned upside.
Trying to do this has been a major problem for me. I tried to transform.Rotate() when the velocity is reduced but it was not very reliable and sometimes acts crazy. Rotating afterrigidbody.isSleeping() works but very unrealistic.
I know I am trying to defy physics here, but there may be some ways to achieve what I want with minimum artificial effect.
I actually need some ideas.
Update - 1
After infor I receive below, I did found some information here, some advanced stuff here. Since the latter link had some advanced stuff, I wanted to start small. So I followed the first link and did below test.
I recorded the position, rotation & velocity of the sea shell with autosimulation enabled and logged them to a file. Then i used the Physics.Simulate() for the same scenario and logged the same.
Comparing the two tells me that data in both cases are kind of similar. So seems like for my requirements I need to simulate the sea-shell drop and then apply that sequence to the actual object.
Now my problem is how can I apply the results of physics.simulate() results (position, rotation, velocity etc..) to the actual sea-shell so the animation can be seen. If I set the positions to my gameobject within the simulation loop nothing happens.
public void Simulate()
{
rbdy = GetComponent<Rigidbody>();
rbdy.AddForce(new Vector3(0f, 0f, 10f));
rbdy.useGravity = true;
rbdy.mass = 1f;
//Simulate where it will be in 5 seconds
int i = 0;
while (simulateTime >= Time.fixedDeltaTime)
{
simulateTime -= Time.fixedDeltaTime;
Debug.Log($"position: {rbdy.position.ToString()} rotation: {rbdy.rotation.ToString()} Velocity {rbdy.velocity.magnitude}");
gameObject.transform.position = rbdy.position;
Physics.Simulate(Time.fixedDeltaTime);
}
}
So, how can I get this simulated data applied to actual gameobject in the scene?
Assume Physics are deterministic, just set the velocity and position and let it simulate on each client. Output should be the same. If the output differs slighly, you could adjust it and it may be only barely noticable.
Physics.simulate may be interesting to read, even if it's kind of the opposite of what you want.
You can throw in the client, record the steps in realtime or using physics.simulate (see point 2) and transmit the animation data as binary - then use it in the other clients to play the animation.

setting scnnode presentation position

I want to set the transform of both a given scn:SCNNode and its current presentation node, to a new value. However, I'm having trouble setting the presentation node. I've tried four ways:
Set presentation node's transform:
scn.position = newVal
scn.presentation.position = newVal
scn.presentation.transform is read only -- it can't be set. (BTW, setting the presentation node's transform compiles with no errors, perhaps something to clean up)
Use resetTransform():
scn.position = newVal
scn.physicsBody.resetTransform()
does nothing. The docs say it "Updates the position and orientation of a body in the physics simulation to match that of the node to which the body is attached". But it isn't changed. Not clear why!
Remove the physicsBody while setting:
let pb = scn.physicsBody
scn.physicsBody = nil
scn.position = newVal
scn.physicsBody = pb
This sets the presentation transform to newVal ("yea!"), but Physics does not work ("boo!"). Perhaps one cannot reuse a physics body.
Add a new physics body after setting:
scn.position = newVal
scn.physicsBody = SCNPhysicsBody(type:.dynamic, shape:nil)
but alas, scn.presentation.position doesn't have newVal.
Thoughts?
I've been noticing this as well in my SceneKit-using project. The direct answer to your question is: “The presentation node automatically updates its transform to match the scene node's transform. If it doesn't seem to be updating, make sure the scene node's isPaused is set to false.” However, it's likely that your scene nodes are un-paused and/or you're not using animations at all, and yet this issue occurs.
I started noticing this issue happening intermittently on iOS 11 when everything in my project worked great on iOS 10.  I haven't touched SCNAnimations or anything like that— I have my own animation system, so I just update my nodes' .position every renderer(_:, updateAtTime:).
So AFAICT, the issue here isn't anything you're doing— the issue is that SceneKit has always been buggy and poorly-written (some parts of the API still haven't been fully updated for Swift), and will likely remain buggy and poorly-written because it seems the SceneKit team has completely moved onto working on ARKit.  So the solution to your problem seems to be “just try a bunch of stuff, setting .position more frequently than necessary, or at a different time in the update loop, or really hack-y solution that seems to work-around the issues in SceneKit”.
For me, personally, I'm slowly working towards abandoning SceneKit in my project— I've mostly switched to custom Metal shaders, and then I'll do my own scene render loop, and eventually I'll replace the scene graph and SCNNode usage leaving me with zero reliance on the buggy mess that is SceneKit.
I wish I had better news for you, but I've been dealing with the weirdness of SceneKit for 2 years now.  It's unfinished garbage.
Update: I ended up “fixing” my presentation nodes misbehaving by using a dirty hack that adds a tiny amount of “wiggle” to the node's position each frame:
public func updateNode()
{
let wigglePositionValue = SCNVector3(
x: Float.random(in: -0.00001...0.0001),
y: Float.random(in: -0.00001...0.0001),
z: Float.random(in: -0.00001...0.0001)
)
self.scnNode.position = _locationModel.value + wigglePositionValue
}
Side-note: As expected, “jiggling” node positions like this also disallows SceneKit from doing some of its batching/rendering optimizations, and (in my experience) incurs a performance hit. It's a horrible hacky work-around, only intended for worst-case scenarios (“the client says we have to go live with the game this week or we don't get paid, and now this stupid SceneKit bug?!”)
So yeah… the real solution here is “don't use SceneKit”.
I have the same issue using ARKit. Because physicsBody adjust itself according to presentation node instead of the node itself, and because the presentation node doesn't update immediately when you change the node's transform, the physicsBody you get might be weird.
There is one solution I found recently:
To change presentation node's transform, use SCNAction. For example, if you want to set position, use node.runAction(SCNAction.move(by: SCNVector3, duration: 0)) instead of simply node.position = SCNVector3. This way, the presentation node immediately updates and you get the right physics.
In addition, this approach only works on the node itself (not applying to children). So if you run action on the node's child, you still can't get the right presentation node and physics. To change the child node's transform and presentation transform, use the node.position = SCNVector3 approach.
To sum up, when changing node's transform, run an action, and when changing the node's children' transforms, directly set the transform.

Positioning particles and life span of particle system in AndEngine GLES2

I am using particle system in my game, in andEngine GLES2, to produce a glittering effect.
I could not find a way to position particles after these are attached to the scene(run time).
Secondly i want to know if there is a way to make a check on the particles movement like listener in animations because I want to stop particles to emit after they expire first time.
Any help in this regard will be much appreciated.
First positioning particle system, There is a method setCenter that you can use in following manner.
particleEmitter.setCenter(pSceneTouchEvent.getX(), pSceneTouchEvent.getY());
Also you can see basic andengine example to clear your concept.
Second stop particle system, As per my consideration there is no listener for particle system. So to satisfy you need you have to think differently. You have to use TimerHandler to satisfy your need. When first time onTimer method of TimerHandler gets executed at that time you have to detach particle system or whatever you want to do.
if you want to move particles in different directions then add
new VelocityParticleInitializer<Sprite>(30,-42, -40, 40)
to the partial system.
Example:
systemBallParticleSystem.addParticleInitializer(new VelocityParticleInitializer<Sprite>(30,-42, -40, 40));
Second point
when partical effect started register a delay modifier and detach your system after that time
Example:
registerEntityModifier(new DelayModifier(pDuration, pEntityModifierListener));
in this pEntityModifierListener you can detach your partical system

cocos2d starting particles from a specific time in the future

I am developing a cocos2d based app with a space background in which I am exploiting a CCQuadParticleSystem to make blinking stars. I have generated this particle system with ParticleDesigner. As soon as I load the particle system white dots representing stars start appearing in the background and after a while they fade out so that, after few seconds in which the particle system reaches the regime state, a night sky full of stars comes out.
My problem is that I would like to know if there is a way to make the particle system starting from a specific time in the future (for instance t0 = 3sec) so that I do not have to wait to have all the starts blinking.
I hope I have clearly explained the problem
thank you in advance
Andrea
I did this and it worked exactly the way I wanted it to.
for (int i = 0 ; i < 300 ; i++)
[emitter update:.1];
Have you tried a
id actions = [CCSequence actions:[CCDelayTime actionWithDuration:3.0f],
[CCCallFunc actionWithTarget:self selector:#selector(onStallComplete)],
nil];
[self runAction:actions];
ok, granted it is probably abusing the original intent of the API's , but is useful
watch for re-entrancy in onStallComplete if you have multiple such delayed reactions.
note: newbie at SO, hope the code snippet comes out looking right
I assume you are using some kind of updateWithDelta: method in your game loop in order to update the particles. If you want the particles to start after a certain interval, make your own timer.
Edit: Based on your comment below, my method is still good, it just needs some tweaking. You need only remove the condition in the updateWithDelta: method on the particle system. That way, it will still update for those 3 seconds, but will not render, and therefore look the way you are describing.
In the .h file:
BOOL particleShouldUpdate;
float particleTimer;
In your init method:
particleShouldRender = NO;
particleTimer = 3.0f;
In your updateWithDelta: method:
if(!particleShouldRender){
particleTimer -= delta;
if(particleTimer < 0){
particleShouldRender = YES;
}
}
// update your particle.
Finally, in your render method:
if(particleShouldRender){
// render your particle.
}
Note that from this point, if you want to stop it rendering, you need only reset the 2 variables like as in the init method, and the same effect will occur.
EDIT2: Upon further clarification, we only need to adapt the init method of your particle. I will make 2 assumptions here, and you need only change them slightly to fit your needs. Suppose that your update cycle is 60 frames per second, the minimum particle lifespan is 1.01, and that you want 3 seconds of updates before you start the game. Then in the init method, try:
for(float delta = 0.0f; delta < 3.0f; delta += (1/60)){
[particle updateWithDelta:(float)(1/60)];
}
This will update your particle like it normally would, but without rendering at each interval, and before anything else gets updated. Alternatively, if you are worried about speed when updating your particle, you can try:
for(int i = 0; i < 3; i++){
[particle updateWithDelta:1];
[particle updateWithDelta:0.02];
}
This will be faster, but may have a few issues depending on your particles parameters.
EDIT3: So looking into this further, cocos2D does not let you do this for some reason. I found a similar question online to this, and they suggested you play with the posVar and speed to make them large enough while you are transitioning into the scene, and once you have fully transitioned into the scene, reset the values to normal. You may want to give that a try!
Hope that Helps!
I do not think there is a way to fast forward your particle system 3 seconds into the future. Alternatively I can imagine two different solutions depending on your circumstances:
Load the scene with the particle behind another scene (e.g. an empty black scene). After 3 seconds switch to the scene with the now nice looking particle effect. This could work f it is ok for you that the user needs to wait for 3 seconds and you only do not want them to see the particle system while everything is clunked together or if you have another scene before the scene with the particle system anyway.
Record the particle system, store it in a file then replay it in your scene. With recording I mean store the position and color of each of your particles. The drawback is, that it will look the same everytime and if you want to run it longer than what you recorded you need to make sure replaying it in a loop still looks good.
I can't think of a way to implement this directly, but could you try something like this as a workaround? I'm afraid I haven't been able to test this yet due to other errors, but working on it.
{
...
//This attempts to make 3 seconds pass 100 times quicker
[[CCScheduler sharedScheduler] setTimeScale:100];
[self schedule:#selector(cancelIncreasedTimeScale) interval:3];
...
}
int numberOfTimesCancelled = 0;
-(void) cancelIncreasedTimeScale
{
numberOfTimesCancelled ++;
//Two because the scheduler is also called straight away? Remove if it's only called after waiting an initial (3/100) seconds
if (numberOfTimesCancelled == 2) {
[self unschedule:#selector(cancelIncreasedTimeScale)];
[[CCScheduler sharedScheduler] setTimeScale:1];
}
}
The issue I do worry about is the other items on your scene would also run 100 times faster. Is that an issue?