OctopusKit Tips & Troubleshooting
- Common Mistakes
- Tips & Tricks
- Best Practices
- Pitfalls & Gotchas
- Conditional Compilation Flags & Debugging Aids
- Bugs
- Other Resources
- 🚀 To quickly start using OctopusKit with a template project, see QuickStart.
- âť“ To see how to do common tasks, check the Usage Guide.
Common Mistakes
Components not having any effect?
-
If the component has an
update(deltaTime:)
method, make sure it’s registered with a Component System, or is manually updated during the scene’supdate(_:)
method, and is only updated once! [TODO: make this automatic] -
Does the entity have all the components that the non-functioning component depends on? A component’s dependencies must be added to the entity before the component that depends on them. [TODO: make this automatic]
-
Pay attention to the order of components when adding them to an entity and when adding their systems to the scene.
Suppose ComponentB depends on ComponentA, but ComponentB has to execute its logic before ComponentA during every frame update cycle; so ComponentB must be added to the entity after ComponentA, but the system for ComponentB must be added to the scene before ComponentA.
-
Check the log for warnings.
Components having too much effect?
- Make sure that a component is updated (if applicable) only once per frame.
- Also make sure that a component system is added to a scene only once!
Input event components not working?
- The
OKScene.shared...
event components should be added to theOKScene.entity
– Did you directly add one of them to a sub-entity, instead of aRelayComponent
to them? Components can only be in one entity at a time! When you add the event components to a child entity, the default scene implementation cannot forward input events to it.
Gesture recognizer components not working?
- Check their properties for the minimum and maximum number of touches.
Scene, subscene or node not receiving input events?
- Check its
isUserInteractionEnabled
property.
Tips & Tricks
-
Review the logs in the debug console.
Look for log entries beginning with ⚠️ or 💡 to see warnings and tips.
Customize the
OKLog
logging system and review the default logs inOctopusKit+Logs.swift
. See the Usage Guide for more details. -
Advanced: Including the OctopusKit code in your main project (instead of as a package dependency) may provide the benefits of Whole Module Optimization.
Best Practices
-
In most cases, try to access the object hierarchy from the “bottom up” instead of “top down.”
TODO: Example
Pitfalls & Gotchas
- Some methods MUST be overridden in a subclass and/or chained to the superclass implementation, by calling
super.method()
. Some methods should callsuper
at different points (i.e. beginning or end) in the subclass’ implementation. Unfortunately, there is no language-level support for enforcing that, so your best bet is to read the source and comments of the method you’re overriding.💡 When in doubt, always call
super
, and call it at the top of your overriding method.
TODO: make this chaining automatic/enforced when/if Swift supports it - Components should try to perform their logic only during the
didAdd(...)
,willRemove(...)
, andupdate(deltaTime:)
methods. If a component’s behavior must be modified outside of those methods, use flags that are then acted upon in the component’s update method.
This ensures that a component does not affect anything outside of a frame update and can be reliably paused/unpaused.Note that this does not mean that a component should not define any other methods at all.
TODO: Example-
Components that respond to asynchronous events, such as a component that moves a node based on input from a gesture recognizer, like
PanControlledRepositioningComponent
, or networking components, MUST perform their function inside theirupdate(deltaTime:)
method, and just use the event-handling action method to mark a flag to denote that an event was received.
This prevents the component from being active outside the frame-update cycle, or when it’s [temporarily] removed from the entity or the scene’s systems. -
A SpriteKit scene processes touch and other input events outside of its
update(_:)
method; when those event handlers update input-processing components, those components will (should) only be able to act on the input data during the scene’supdate(_:)
method.
-
- OctopusKit components may sometimes cause some “friction” against the SpriteKit API.
e.g.: when adding aNodeComponent
and aPhysicsComponent
to an entity; the SpriteKit node may have its ownphysicsBody
and thePhysicsComponent
may have a differentphysicsBody
.-
As a general rule, adding a component to an entity assumes that the component adds its encapsulated functionality to that entity, replacing any identical functionality that already exists.
e.g.: If you add a “blank” component, e.g. a
PhysicsComponent
with anil
physicsBody
, then the component attempts to “adopt” any existing properties of the underlying SpriteKit object.
In this example, the blankPhysicsComponent
will set its property to thephysicsBody
, if any, of theNodeComponent
node. After that, other components should access the node’s physics properties through thePhysicsComponent
instead of the node’sphysicsBody
property.
-
-
Entities and components may not always be the answer to everything. Sometimes good old classes and structs may be the better solution. (^ - ^”)
- An entity can/should only have one component of a specific type (not counting generic variants.)
If an entity needs multiple components of the same type but with different parameters, consider using a “master” component that holds a collection of regular classes/struct.e.g.: a ShipEntity with a WeaponsSystemComponent that holds an array of Gun classes/structs, to represent multiple guns where each gun is mounted on a different side of the ship.
Conditional Compilation Flags & Debugging Aids
Set these in the
Package.swift
manifest for OctopusKit. Example:targets: [ .target( name: "OctopusKit", dependencies: [], swiftSettings[.define("LOGINPUTEVENTS")])
LOGECSVERBOSE
- Logs detailed Entity-Component-System actions.LOGECSDEBUG
- Logs Entity-Component-System debugging information.LOGCHANGES
- Enables the@LogChanges
property modifier and other types of verbose value logging.LOGINPUTEVENTS
- Logs all input events and related information via the@LogInputEvents
property modifier.LOGPHYSICS
- Logs all physics contact/collision events.LOGTURNBASED
- Logs each begin/update/end cycle for turn-based components.
⚠️ Setting any of the logging flags may reduce engine performance.
-
Set
OKLog.warnings.breakpointOnNewEntry = true
to trigger a breakpoint whenever a new entry is added to thelogForWarnings
, allowing you to review the application state and call stack to catch causes of unexpected behavior.breakpointOnNewEntry
requires theDEBUG
conditional compilation flag (automatically set by Xcode for debug builds).
Bugs
- Search the entire source code for
BUG:
,APPLEBUG:
,FIXME:
,WORKAROUND:
etc. comments for any outstanding bugs and their temporary workarounds, if any.
There seem to be some bugs in Apple’s own APIs and frameworks that we cannot do much about:
-
SpriteKit: Scenes with cameras are not fully compatible with shaders and
shouldEnableEffects
. 2020-05-12, 20200512C- Setting
shouldEnableEffects = true
on anSKScene
instance, e.g. for applying a shader to the entire scene, messes up the scene’scamera
: - The
SKCameraNode
’s position remains fixed. - If the
SKCameraNode
is scaled (zoomed) in or out, the scene becomes blank (black) outside the camera’s former frame (apparently equal to the screen size). - This happens whether the scene’s has a
shader
or not. - đź’ˇ Workaround: Untested: Use an
SKEffectNode
and Core Image filters to apply effects to the entire scene.
- Setting
-
SpriteKit: Shaders with associated uniforms or attributes do not work with
SKTileMapNode
. 2020-05-12, 20200512A, 20200512B- If the shader has uniforms, a runtime crash occurs:
validateFunctionArguments:3476: failed assertion 'Fragment Function(SKShader_FragFunc): missing buffer binding at index 2 for u_xxxxx[0].'
- If the
SKTileMapNode
has anSKAttribute
, it is not propagated to the shader. - Shaders without uniforms or attributes work fine with
SKTileMapNode
. - 💡 Workaround: Convert the uniforms and attributes to constant values in the shader’s source code.
- If the shader has uniforms, a runtime crash occurs:
Other Resources
OctopusKit © 2021 Invading Octopus • Apache License 2.0