How can I use GameplayKit State Machine for sprite animation? - sprite-kit

I have two animations for my player to run and jump. Can I control these animations using GameplayKit's state machines? If so, how? In this project, I use SpriteKit and GameplayKit for Entity-Component architecture and State Machine.

You need set first the StateMachine and fill with your custom classes
self.playerStateMachine = GKStateMachine(states: [
PlayerRunning(player),
PlayerJumping(player)
])
Then when you need to enter to state, can be with:
self.playerStateMachine?.enter(PlayerRunning.self)
On the state you can perform changes like:
override func didEnter(from previousState: GKState?) {
self.player?.run(runAnimation)
}
Example:
Please take a look of complete example here: https://github.com/Maetschl/SpriteKitExamples/blob/master/StateMachineAnimation/StateMachineAnimation/GameScene.swift

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.

How do I access variables in Swift contained in a custom class on a SpriteKit Scene Editor Reference node?

First I would like to say that I am completely self taught following as many tutorials on the internet that I can find, so apologies in advance if there are certain concepts Im not too familiar with.
Alright with that out of the way I'd like to ask help on something that's been stumping me for days.
I have a reference node(referenceNode) in a SpriteKit Scene(GameScene),
That reference node references another SpriteKit Scene( referenceScene),
and this scene has a Custom class(referenceClass.swift).
I have a boolean variable in referenceClass.swift called trueOrFalse, and I want to be able to change the vale from my code in gameScene.swift.
I have tried casting from the SceneEditor to my code by creating instances as you would do SKSpriteNode's and SKLableNodes but this does not seem to be working
class GameScene: SKScene
{
var referenceNode: SKReferenceNode! //link to SceneEditor Reference Node
override func didMove(to view: SKView)
{
referenceNode = self.childNode(withName: "referenceNode") as! SKReferenceNode
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
{
referenceNode.trueOrFalse = false
}
}
this is my custom class
import UIKit
import SpriteKit
class referenceClass: SKScene
{
var trueorfalse:Bool = true
func oneFunction()
{
}
}
This is just a small example project that I made so I could get my head around the concept trying the different solutions I could find but with no success.
I realise I could create global variables but I am trying to keep my code Clean as In all functionality related to this node to be kept in this referenceClass. Also I've been told its not good programming?
Any help would be greatly appreciated :)
Barney
First of all I would check whether the search is done properly -
https://developer.apple.com/documentation/spritekit/sknode/searching_the_node_tree
For instance, in the template SpriteKit project nodes are being searched with // prefix, i.e. //referenceNode in your case.
Secondly, do not force-cast it to SKReferenceNode immediately, first check what kind of object is returned (if any).

Unity calling methods on new scene

I have a game with a bunch of scenes, I load them and then do DoNotDestroy so I keep them (awake/start get called the first time).
Let's say we have following state changes - menu -> game -> game over -> menu -> game...
I want to call a method when the scene is shown each time to reset/set some values.
What's the cleanest way to do this? Currently I'm doing it with the update method, but it takes a bunch of lines and it's wasteful to use Update() in every script when you don't need to.
As per the Unity documentation on Multiple Scene Editing, it's recommended that you avoid DontDestroyOnLoad.
It is recommended to avoid using DontDestroyOnLoad to persist manager
GameObjects that you want to survive across scene loads. Instead,
create a manager scene that has all your managers and use
SceneManager.LoadScene(<path>, LoadSceneMode.Additive) and
SceneManager.UnloadScene to manage your game progress.
Therefore, in your case, you could handle resetting data in a number of ways. But one simple way would be to check the name of the scene that was just loaded, from a component in your "Manager" scene. E.g. MySceneManager in BaseScene. In the code following, we're making use of the sceneLoaded delegate of SceneManager.
using UnityEngine;
using UnityEngine.SceneManagement;
public class MySceneManager : MonoBehaviour
{
void OnEnable ( ) => SceneManager.sceneLoaded += OnSceneLoaded;
void OnSceneLoaded ( Scene scene, LoadSceneMode mode )
{
switch ( scene.name.ToLower ( ) )
{
case "menu":
// Do some "menu" initialisation here...
break;
case "game":
// Do some "game" initialisation here...
break;
case "gameover":
// Do some "gameover" initialisation here...
break;
}
}
void OnDisable ( ) => SceneManager.sceneLoaded -= OnSceneLoaded;
}
N.B. This method unfortunately won't have compile-time checking of your scene names, so you'll have to be careful to make sure your scene names correspond to the switch cases.
But, another thing the remember is that any component in your newly loaded scene will have Awake() called anyway, so you could just offload all of your scene initialisation to a component in each scene. This would decouple MySceneManager from the data in each scene.
So, you've got quite a few options, these being just two of them. The "best" option will be the one you're comfortable with, and that suits your needs.

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.

Need some general classes advice

I appreciate this is not Swift specific, but I am writing a small game in Swift thus I have tagged it with the relevant tag. I am new to the Swift language but picking it up quickly, and while not new to programming in general, do often question my approaches.
Here is my scenario:
I am writing a small 2D platform game with a player character and various obstacles. The player can have different textures but generally shares the same capabilities, such as jump and sprint. Likewise, the obstacles generally have different textures, but otherwise share the same capabilities.
Both the characters and obstacles also share common capabilities but not all, for example an obstacle can't jump. With this in mind, this is how I have written my classes...
class GameObject {
let node : SKSpriteNode!
}
Class GameObstacle: GameObject {
init() {
super.init()
}
func explode() {
// explode code
}
}
class GameCharacter: GameObject {
init() {
super.init()
}
func jump() {
// jump code
}
}
class GameCharacter_Sheep : GameCharacter {
init() {
super.init()
self.node = SKSpriteNode(namedFile: "sheep")
}
}
My logic behind this approach is that all of the common object functionality is included in the GameObject class, all of the common character functionality (such as jump) is included in the character class and the unique stuff, down at the class level.
One question I have, am I correct to initialize my node (defined in my GameObject class) in my GameCharacter_Sheep class? My rationale is that because the texture, and thus physics body will differ from character to character, that is the correct place to do the implementation?
Again, I appreciate that this may be basic OOP and not really Swift specific but I'm just looking for some guidance.
Many Thanks,
Jon
If all your game objects are going to have an instance of SKSpriteNode which is passed data from a named file, you can move it's creation into the base class, and just have the file named passed into the call to super.init().
More generally, inheritance isn't the only option for sharing functionality between different game objects. Don't forget about composition, which is often preferred when defining game objects to avoid deep, inflexible inheritance hierarchies which sometimes cannot model the desired behaviour. There are a whole bunch of ways to approach all the way through to a full blown game object component based system.