OctopusKit Tips & Troubleshooting
- Common Mistakes
- Tips & Tricks
- Best Practices
- Pitfalls & Gotchas
- Conditional Compilation Flags & Debugging Aids
- Other Resources
- 🚀 To quickly start using OctopusKit with a template project, see QuickStart.
- ❓ To see how to do common tasks, check the Usage Guide.
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’s
update(_:)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?
OKScene.shared...event components should be added to the
OKScene.entity– Did you directly add one of them to a sub-entity, instead of a
RelayComponentto 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
Tips & Tricks
Review the logs in the debug console.
Look for log entries beginning with ⚠️ or 💡 to see warnings and tips.
OKLoglogging system and review the default logs in
OctopusKit+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.
In most cases, try to access the object hierarchy from the “bottom up” instead of “top down.”
Pitfalls & Gotchas
- Some methods MUST be overridden in a subclass and/or chained to the superclass implementation, by calling
super.method(). Some methods should call
superat 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
update(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.
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 their
update(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’s
- OctopusKit components may sometimes cause some “friction” against the SpriteKit API.
e.g.: when adding a
PhysicsComponentto an entity; the SpriteKit node may have its own
PhysicsComponentmay have a different
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
physicsBody, then the component attempts to “adopt” any existing properties of the underlying SpriteKit object.
In this example, the blank
PhysicsComponentwill set its property to the
physicsBody, if any, of the
NodeComponentnode. After that, other components should access the node’s physics properties through the
PhysicsComponentinstead of the node’s
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.swiftmanifest 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
@LogChangesproperty modifier and other types of verbose value logging.
LOGINPUTEVENTS- Logs all input events and related information via the
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.
OctopusKit.logForWarnings.breakpointOnNewEntry = trueto trigger a breakpoint whenever a new entry is added to the
logForWarnings, allowing you to review the application state and call stack to catch causes of unexpected behavior.
DEBUGconditional compilation flag (automatically set by Xcode for debug builds).
- Search the entire source code for
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
shouldEnableEffects = trueon an
SKSceneinstance, e.g. for applying a shader to the entire scene, messes up the scene’s
SKCameraNode’s position remains fixed.
- If the
SKCameraNodeis 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
- 💡 Workaround: Untested: Use an
SKEffectNodeand Core Image filters to apply effects to the entire scene.
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.'
- If the
SKAttribute, it is not propagated to the shader.
- Shaders without uniforms or attributes work fine with
- 💡 Workaround: Convert the uniforms and attributes to constant values in the shader’s source code.
- If the shader has uniforms, a runtime crash occurs:
OctopusKit © 2021 Invading Octopus • Apache License 2.0