Issue having a particular SKAction to fire in a sequence - swift

I'm sure this is a thread issue but I can't figure it out.
I want to make a sprite fade and reappear somewhere else. I can get it working one way, but not the other (see below) The sequence I want is:
fade, x, y, z (all are SKActions)
One of my classes provides the implementation details for the warp in animation (adhering to a Warpable protocol I created)
protocol Warpable
{
func warpOutAnimation() -> SKAction
}
//and then elsewhere, in my actual class:
func warpOutAnimation() -> SKAction
{
return SKAction.fadeInWithDuration(1.0)
}
So here is my sequence within the game:
myNode.runAction(SKAction.sequence([SKAction.fadeInWithDuration(1.0), x, y, z]))
This runs perfectly fine. All good.
Now, if I wanted to instead grab the warp implementation from the class (since this is dynamic from a factory class I have):
myNode.runAction(SKAction.sequence([
(self.currentMonster as! Warpable).warpOutAnimation(), x, y, z]))
This sequence does not execute the warpOutAnimation (despite it returning an SKAction) but it does fire x, y, z still.
currentMonster is instantiated from a factory based on some context within my game. I have a breakpoint there, and it does indeed hit the protocol's function to fire, but the SKAction isn't run.
Any ideas why this would happen? Even if I put the warp function right above where I'm doing the sequencing it doesn't do anything. Thanks so much.

As per docs, the fadeInWithDuration action changes the alpha property of the node from its current value to 1.0.
This action has no effect if alpha value of a node is already 1.0, and that is probably your issue.
What you probably want is, to use fadeOutWithDuration, to make a node disappear, before you try to use fadeInWithDuration method to make it re-appear.
So, make sure that myNode has alpha < 1.0 if you want to apply fadeIn action.

This function seems to be missing a return statement. It would not compile so I presume that something is missing from your sample code.
func warpOutAnimation() -> SKAction
{
SKAction.fadeInWithDuration(1.0)
}

Related

How do you update your UI when using a state machine

I'm using swifts stateMachine from gamePlayKit. It works great, but there is one thing I don't understand: The stateMachineseems to be its own isolated island; from what I can tell there is no way to pass in arguments or get callbacks. And this raises questions like, how do I update the UI?
Lets say I enter combat state:
stateMachine.enter(CombatState.self)
There's no way to pass in an argument. Ok, fine. But then CombatState does its thing and when it's done it goes into another state. BUT, before that I need a callback to the view so that I can remove the units from the board lost in combat like so:
self.removeChildren(in: nodesOutOfAction)
But there's no way to send the nodes out of the stateMachine because there's no possible way to add a completion handler. So how do you work around this problem? To me the stateMacine is pointless as it canĀ“t update the UI.
Its possible to access properties of the state like so:
stateMachine.enter(CombatState.self)
self.removeChildren(in: combatState.nodesToRemove)
But that can't be safe, right? Still, as this is the only option that I know of, this is how I solve it.
I totally recommend you check the DemoBots project from Apple.
You should pass the entity reference on the State Initializer
For example
class CombatState: GKState {
unowned var gameScene: GameScene
required init(gameScene: GameScene) {
self.gameScene = gameScene
}
override func didEnter(from previousState: GKState?) {
gameScene.removeChildren(in: ...)
}
}
With this logic you can decouple a lot of logic inside to the states.

Managing GameScenes update method and referencing changing instance properties within other classes

I'm making my first game in Swift and I'm trying to get a SKSpriteNode subclass instance (trailingSprite) to follow or "trail" another SKSpriteNode subclass (User) that the user controls. I was able to achieve this by making a move(aUser:User) method within the trailingSprite class that applies a velocity vector to to the trailing body thats the difference of the two postions I then call this method inside of GameScenes Update function. However, this trailingSprite isn't always on screen and can spawn at random times in differing quantities. Is there a more effective way of retrieving "live" or updated positions of the User instance within each trailingSprite instance instead of having to call a move method for each one inside of Update (which ultimately wont work)? I also tried passing the User instance by reference but this still didn't work as the velocity vector never updated from the original positions. Any help would be awesome!
Inside the update method you should be able to call a move method on every trailingSprite in your scene by finding all the nodes with the corresponding name.
self.enumerateChildNodes(withName: "trailingSprite", using: { node, _ in
if let trailingSprite = node as? TrailingSprite {
trailingSprite.move(aUser: user)
}
})
This would require that you set the name property on your TrailingSprite class:
self.name = "trailingSprite"

Where should I put my functions?

I'm working on an app and I wrote a large part of an SKScene in a single class. It works great, but when I took a (java) course this past semester, it seems that accepted industry practice is to separate it into many classes.
More specifically, my (SpriteKit) app contains a cat and a mouse and after finishing said course, I decide that instead of containing all their info in the SKScene, I should separate some of it into multiple classes (a Player superclass with Cat and Mouse subclasses) each containing their relevant info (such as x and y position,) and functions (such as moveCat) with only Scene related functions and info in the Scene class.
The problem lies in the content of the functions.
Particularly one of the functions, pathBlocked(which checks if there are any barriers blocking the desired path of movement) uses a lot of info that wouldn't make sense to contain inside the Player object (such as all the info about the barriers on the board, and how much cheese was collected).
I can't just leave pathBlocked as a Scene function because there's a function that should belong to the cat (catAI) which uses pathBlocked to navigate. If it's a method of the scene, then it won't work. I'd need to instantiate a Scene object every time I wanted to call pathBlocked.
Should I just forget about making the Cat and Mouse Classes or should I fill the Player class with info that doesn't quite belong?
or is there a third option I'm not thinking of?
If you need a snippet of the code, I could include some of it.
Thanks!
Ok, so what you should do is
class Cat {
var sceneRef: GameScene? //or whatever scene is called
var xPos: CGFloat!
var yPos: CGFloat!
init(s: GameScene){//just example stuff
sceneRef = s
xPos = sceneRef!.childNodeWithName("cat").position.x
yPos = sceneRef!.childNodeWithName("cat").position.y //However, these variables will not stay up to date. If you need them current, you would have to do the same thing again, xPos = sceneRef!.childNode...etc.
}
func doStuff{
}
func createNewPath()
//create a new path
}
}
Then in the scene, you can do:
class GameScene: SKScene {
var cat: Cat?
override init(size: CGSize){
super.init(size: size)
cat = Cat(s: self)
func whatever() {
if (pathBlocked()){
cat!.createNewPath()
}
}
I think you will just have to unwrap it each time you use it, but XCode will tell you that.
^ credit for that should go to AMomchilov, I didn't know about weak references at all before this. It was a fun learning experience XD.
If you are looking to organize your code, another way you could do it is have an extension file for your scene, and throw all the low level function stuff in there, and keep all the high level stuff in the actual scene class. Make a new class, and call it SceneExtension or something:
import SpriteKit
//import whatever else you need
extension GameScene { //<- Or whatever your previous scene that you want to extend is called
func pathBlocked() {
//function code
}
//other functions
}
And just basically throw all the other functions that you don't want to look at and just take up space in the actual scene file. It acts like its in the same file, so you can call any of the functions in here, or use any variables from either class.

Does Unity have a constructor when loading a scene?

I would like to populate the UI when I load a scene, with the correct data, instead of placeholders.
When I call "LoadSceneAsync", what would be the first object that is called, so I can fill the UI label with the correct data? I know that there is a scene GameObject, but I am not sure if that would fit my needs.
I am looking for some sort of constructor, called when a new scene object is loaded; to plug in my setup function.
You say
Indeed I did use "onlevelwasloaded" but the UI element may not be there, ready to go, when I invoke it, which lead to errors
That would be an incredibly sever bug in Unity! :)
Could it be that you are mixing-up Awake and Start somewhere?
One way to think of it is once you call Start, you know all the Awake have already run.
When I call "LoadSceneAsync", what would be the first object that is called, so I can fill the UI label with the correct data
You are still within the same frame.
Once you see LoadSceneAsync you can be absolutely assured everything is Awake 'd.
Or indeed once you use Start you can be absolutely assured everything is Awake 'd.
1) could it be that in some of your UI elements (or whatever) you are doing something in Start which you should do in Awake?
2) if (for some reason) you want to "wait until the next frame", perhaps just during development - then do that, wait a frame. You'll see a flicker, but if that's what you want to do (for some reason) do that.
3) note that if you mean you want to go to the net to get something, well of course you have to wait frames (use Update/coroutine) until the information comes back from the net, obviously. (How else could it be?)
Note that in practice, one should be using UnityEngine.Events.UnityEvent everywhere.
Maybe this is what you are looking for http://docs.unity3d.com/ScriptReference/MonoBehaviour.OnLevelWasLoaded.html
Relying on Unity internal functioning is not always the way to go. Particularly when dealing with RESTApi (which is somehow what you are dealing with here).
You cannot assume one object will be ready before another except if you control it.
Add a Controller script that uses Awake. In the Awake, call all the methods you are needing and use some callback to generate secondary code when primary is ready.
public class MyController: MonoBehaviour{
private ServerRequestController serverCtrl = null;
private UIController uiCtrl = null;
private void Awake(){
serverCtrl = this.gameObject.AddComponent<ServerRequestController>();
uiCtrl =this.gameObject.AddComponent<UIController>();
serverCtrl.GetData(uiCtrl.SetUI);
}
}
public class UIController:MonoBehaviour{
public void SetUI(Data data)
{
SetTopImage(data.topImage);
SetBottomImage(data.bottomImage);
// And so on
}
}
public class ServerRequestController:MonoBehaviour{
public void GetData(Action onCompletion){
// This may be a coroutine if you fetch from server
Data data = GetDataFromSomewhere();
// At this point, your data is ready
onCompletion(data);
}
}
Thanks to this, you are now able to know exactly when a piece of code is ready.

Should I use Start or Update to call the given method in Unity3d

1.I have a class in which I have a method called ProjectileMotion() with return type IEnumerator and this method is being called from Start().
2. The above script is attached to a prefab that is instantiated from another class.
Problem:
In IENumerator ProjectileMotion() method, I need to have updated position of an object which is moving continuously,so I declared
//assigned the o=gameobject to it and it is moving continuously
Public Transform Target;
In ProjectileMotion(), if I do Target.transform,position, it gives the starting position only and not where it was at the time of instantiation of the prefab to which this script is attached, However, I am able to get the updated position of Target in Update method(checked using Debug.Log).
But I can not call the ProjectileMotion() in Update method of course, what should I do to get updated position of Target every time the prefab is instantiated and so the script is called.
Simply put ...
If you want the code to execute when the component is "started" then call it on start.
OR
If you want the code to execute each frame after the component has started then call it on update.
Judging by the name of this method I would think this is something that updates the position of a projectile after a gun has instantiated it.
The position being updated is likely a per frame operation so.
I would put this in the update method.
EDIT:
UNLESS ...
Does this function do a multiframe animation?
Unity has the concept of coroutines that allow to do certain actions of several frames. in this case you might be better off doing something like this in your start method ...
StartCoroutine(ProjectileMotion)