What's the difference between these two SpriteKit functions? - swift

I don't know what's the difference between these two functions.
First:
coin.run(SKAction.moveTo(y: -146.115, duration: 0))
Second:
coin.position.y = -146.115

The SKAction will not be processed until the next frame-- directly after update. If you call .run after didEvaluateActions, your position will not be updated, and you may encounter bugs due to that.
The second line of code will take place immediately, regardless of your position in the SK loop.
Example, if you are using physics, and call .run(.move( on something in didBegin(contact, and then are expecting that sprite to have moved already by didEnd(contact), then you will have problems. In that situation, you want to manually adjust .position instead of using an action.
Secondly, the .run command is also less performant, because it requires the initialization of an SKAction object, which is somewhere between 20-30% slower than just adjusting the position manually.
Granted, that amount of difference doesn't add up to much, but in complicated scenes it could be the difference between getting everything done in 16ms (60fps) or not.
Third, as others have mentioned, there is the forDuration parameter, which allows you to animate the movement over a period of time.. say, 2 seconds, or however long you want.

SKAction.moveTo() has a duration parameter, which is there, because it is an animated version of changing a node's position over the specified time interval. On the other hand, changing the position of a node doesn't animate the movement.

In first line you are using your coin class object and accessing function run through it's object.
coin.run(SKAction.moveTo(y: -146.115, duration: 0))
In second line coin class object accessing it's property position.y and assign it float value.
coin.position.y = -146.115
Hope u got it!!!

Related

Problem accessing a node from a class [GDScript]

I encountered some difficulties while studying Godot.
I'm trying to add a shotgun class (Shotgun.gd). I have a function in the class that creates several RayCasts, a function that checks what they encounter and a function that calls these functions one by one. If I call these functions from the class where they are declared in the _process() function, everything works fine, but if I call these functions in another script, like Player.gd, it finds nothing when checking for RayCasts.
P.S Sorry for the sloppy translation, English is not my native language
Part of the code from the shotgun class (Shotgun.gd)
# Gen RayCast-ы
func gen_raycasts(player):
# Очистка списка
raycasts.clear()
var temp
for number in PELLETS_COUNT:
# Создание RayCast
temp = RayCast.new()
# Установка настроек
temp.set_cast_to(Vector3(LENGHT, 0, 0))
temp.set_enabled(true)
# Добавляет RayCast на сцену
player.add_child(temp)
# Установка положения
temp.transform.basis = Basis(Vector3(-0, 0, 0.1), Vector3(-0, 0, 0.1), Vector3(0.1, 0, -0))
temp.global_translate(Vector3(0, 1.65, 0))
temp.set_scale(Vector3(0.1, 0.1, -0.1))
# Рандомный разброс дробовика
randomize()
temp.rotate_y(deg2rad((-50 + randi() % 100) / 10))
temp.rotate_x(deg2rad((-50 + randi() % 100) / 10))
raycasts.append(temp)
#print(raycasts)
# Check Rayasts
func chech_raycasts():
in_raycast.clear()
for number in PELLETS_COUNT:
in_raycast.append(raycasts[number].get_collider())
#print(raycasts[number].is_enabled())
print(in_raycast)
Godot tree picture
Call from func _process()
Call from Player.gd
I'll venture to say that it is probably correct they collide with nothing.
Enable "Visible Collision Shapes" in the "Debug" menu, so Godot shows colliders in runtime (including enabled RayCasts), and check they are being positioned correctly.
I can think of a couple possible problems:
You are adding the RayCast before positioning it. This is not always a problem, and it is probably not a problem in your case. However, it might register a collision when added, before moved.
You are scaling the RayCast. This should not be a problem. However, there have been bugs related applying a scaling directly on collision shapes… So, I will avoid this, just to be safe.
A suggestion: Add a Position node, child of your Shotgun, marking the origin for the RayCasts. Then you can copy the global transform of the Position to the global transform of the RayCasts - and then apply the random rotation - before adding them as children. This should make it easier to update the position if you need to (e.g. if you changed the shotgun model, you can mode the Position node accordingly). It should avoid any typos when writing the coordinates in the code. And it should avoid the pitfalls mentioned above.
I'll remind you you are supposed to check is_colliding. I guess you are not doing it for demonstration purposes.
I'll also remind you that the RayCasts update with the physics frame. So, when calling the RayCast from somewhere other than _physics_process you are getting what they registered last physics frame (i.e. just before _physics_process ran last). You can make Godot update the RayCast by calling force_raycast_update on them.
If you don't need the RayCasts often, having them update every physics frame might be overkill. In that case, you might be interested in checking like this: get_world().direct_space_state.intersect_ray(start, end). See PhysicsDirectSpaceState. However, using RayCast should work. Using intersect_ray is only an optimization.
Unrelated: if you want to guarantee an uniform probability distribution distribution from the random functions, call randomize once at the start, instead of once per iteration. This is probably not noticeable anyway.

Which position value changes when you use an SKAction on an SKSpriteNode? And how would I account for the position changes?

I'm trying to use an SKAction to move my sprites. However, I noticed that after moving the sprites, their position values still remained the same. With some research, I learned that (correct me if I'm wrong) the sprites' position values are actually their location relative to the parent view, and that it was the parent view's position value that had actually changed, and not the sprites'. Only when I checked, the parent view's position value also hadn't changed.
So which position value actually changes?? And how would I go about accounting for the change in position when accessing the sprites' position values? (Since a lot of my game's logic is dependent on their position values)
Here's my code:
let fall = SKAction.moveBy(x: 0, y: -85, duration: 0.2)
print("Original position \(gift.position)")
gift.run(fall)
print("New position \(gift.position)") // Both print the same value
Any help would be greatly appreciated!
Your gift.run(fall) is scheduling an action that will happen over the course of 0.2 seconds, not waiting for it to finish. At 60 frames/second, that means the sprite will reach its final position only after 12 frames. If you print the gift's position at each step in update(_:), you'll see that it is indeed changing.
Since the run does not wait for the action to complete, the sprite hasn't had a chance to move at all when the second print is executed.
If you want something to happen only after an action completes, you have various possibilities. A common way is to add a completion block; see Apple's documentation on getting started with actions: https://developer.apple.com/documentation/spritekit/getting_started_with_actions. Or if you want the same node to do multiple things in order, you can make a composite SKAction, like with the sequence or repeat initializers.

Restart SKEmitterNode without removing particles

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)
}
}

Are there any obvious performance issues of mass-using SKActions?

I have a game where I use a lot of SKActions to deliver the desired game logic.
For example, instead of setting the zRotation of a sprite to a value, I would use runAction(SKAction.rotateTo(/* angle here */, duration: 0.0) instead.
I make calls like this in update, and in touchesMoved. Thus this could mean hundreds of these calls, sometimes nested with groups of other actions.
Am I incurring significant overhead relative to directly setting the zRotation property?
Never use SKActions for real-time motion. Instead you should either set the zRotation directly or set the necessary angular velocity each frame. In your case the update method and touchesMoved method are both bad to use for SKActions because they can run 30-60 times a second.
SKActions do generate significant overhead. Here is a quote from Apple's documentation:
When You Shouldn’t Use Actions
Although actions are efficient, there
is a cost to creating and executing them. If you are making changes to
a node’s properties in every frame of animation and those changes need
to be recomputed in each frame, you are better off making the changes
to the node directly and not using actions to do so. For more
information on where you might do this in your game, see Advanced
Scene Processing.
Source
You can see an example of using real-time motion instead of SKActions in my answer here
You should not call a SKAction in the update method as it is called 60 times a second. Instead, use a event based trigger which in turn calls the SKAction. The touchesMoved is a good example of that. You can also use a completion method block to signal for a new SKAction upon completion of the current action.

Moving an Object to A Point

Another relatively simple question I hope.
I have a person object that I move to a point designated by a point on a screen touched by the user.
I would like to have a set speed that the object moves to the position in.
However I am not sure how to do it as every time I calculate it it either has varying speeds or the x origin gets there before the y origin and vice versa.
I would like to be able to move them at the same speed and have them reach the point at the same time.
I hope I have explained this well, if not please ask questions
Thanks in advance
Disco
In your update function, calculate the X and Y distance from point A to point B, and each frame increment the difference by the same fraction rather than a constant value.
i.e. player.X += difference / frameCount.
Well, speed is distance * time, and what you need is an instance of NSTimer. Use timerWithTimeInterval:invocation:repeats: and have it invoke a moveObject method. The moveObject method move the object by a certain distance each time it is called until the object's object's location is equal to the destination's location. Then the moveObject method invalidates the timer and Bob's your uncle.
---UPDATE----
Wouldn't you know it, no sooner had I written this and what do I see? UIView animateWithDuration:animations: . Which is the above, but, like, way easier. At least, I hope so for I haven't used it myself. Yet.