Giter VIP home page Giter VIP logo

haskellspritekit's Introduction

Haskell binding to Apple's SpriteKit framework

Open source under BSD3 license. Contributions under the same license are most welcome.

To build this project, you need a recent version of Xcode and GHC 8.0.2. If you don't want to build it yourself, a pre-compiled version comes with Haskell for Mac, which also provides the best Haskell SpriteKit development experience.

This code has currently only been tested on macOS. The main obstacle to using it on iOS is lack of support for Template Haskell (and hence, language-c-inline) by GHC for iOS, but What is New in Cross Compiling Haskell might help.

The talk Haskell SpriteKit — A Purely Functional API for a Stateful Animation System & Physics Engine discusses the architecture of Haskell SpriteKit — see also the accompanying paper Haskell SpriteKit — Transforming an Imperative Object-oriented API into a Purely Functional One.

An example game: Shades

Shades Loop

Have a look at a clone of the mobile games Shades as a simple example to get you started:

https://github.com/gckeller/shades

This implementation of Shades is explained in detail in the paper Haskell SpriteKit — Transforming an Imperative Object-oriented API into a Purely Functional One, which provides an overview of the design and internals of Haskell SpiteKit.

Another example game: Lazy Lambda

Lazy Lambda Loop

As a second example of a simple game in Haskell SpriteKit, have a look at Lazy Lambda, a Flappy Bird clone:

https://github.com/mchakravarty/lazy-lambda

For an explanation of the main concepts of the Haskell SpriteKit binding including live coding of Lazy Lambda, watch the talk Playing with Graphics and Animations in Haskell (includes video and slides).

Building with a stock GHC distribution

Unfortunately, a quick and easy cabal or stack-based build is not possible. In addition to Haskell, this project includes Objective-C and Swift code, and neither cabal nor stack can handle this properly.

The build requirements are as follows:

  • Xcode 8 (from the Mac App Store) on macOS 10.11 or 10.12. (The latest Xcode 7 might also work, maybe with light tweaking, but I haven't tried in a while.)

  • GHC 8.0.2 along with alex, cabal, cpphs, and happy. (The build system expects to find those in your $PATH.)

Open the project in Xcode and build or archive. In either case, the build procedure will install a fair few packages from Hackage into your user package database (see the .cabal file in the spritekit directory for details), including a spritekit package. Moreover, Xcode will produce a HaskellSpriteKit.framework. The documentation is only built in Release mode.

To use, HaskellSpriteKit.framework in an app, you need to do the following:

  • Write some GUI code in Swift (or Objective-C), to open a window and put an SKView into that window (in which you can present the SpriteKit scene generated by the Haskell code).

  • Write Haskell code that uses the spritekit package to implement a SpriteKit scene. Call this code from Swift, implementing the GUI, using the Haskell FFI or by using the Objective-C support in language-c-inline. The latter is what Haskell SpriteKit uses internally. To learn more, check out my Haskell Symposium 2014 talk: Foreign Inline Code in Haskell.

  • Link all this together into one app that includes HaskellSpriteKit.framework as an embedded framework. I recommend to compile all Haskell code using custom build scripts in Xcode in the same way that the Haskell SpriteKit build system works. This makes it easy to do all packaging, code signing, etc right there in Xcode as well. In fact, you may like to put all your own Haskell code into your own embedded framework inside your app. This speeds up builds and simplifies linking in my experience.

The build system assumes that everything is going to be linked dynamically.

An example

If you like to see concrete example code of how to build a standalone Mac app embedding a Haskell SpriteKit scene, check out the ShadesApp folder of Shades. Follow the instructions above for building the Haskell SpriteKit framework with a stock GHC (requires GHC 8.02) and then, the instructions at Shades for how to compile the Swift wrapper and generate an app bundle containing everything.

NB: The basic set up is similar to Haskell for Mac, and hence, suitable for distribution through the Mac App Store.

Feature set

For details of the supported API, see the current Haddock documentation (currently version 0.9.0.0).

Supported features

The Haskell binding supports the following SpriteKit node types:

Moreover, almost all SpriteKit animation actions (SKAction) are supported as well as textures and graphics paths (to define polygons and Bézier curves). All the basic features of the physics engine are supported, such as gravity, collisions, contact handlers, various material properties, as well as volume-based and edged-based physics bodies.

Unsupported features

  • Unsupported node types: video nodes, emitter nodes, crop nodes, effect nodes, light nodes, field nodes, camera nodes, audio nodes, and reference nodes.
  • The various search routines are not supported.
  • Physics: joints, constraints, and fields are not supported.
  • No macOS 10.12 features are supported.

Most of the missing features can be supported quite easily by simply following the same approach as used in the existing code. (Just open an issue if you are looking for something specific. Or, if you can, implement it and open a pull request.)

Moreover, this project needs a proper suite of unit tests.

haskellspritekit's People

Contributors

mchakravarty avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

haskellspritekit's Issues

Strictness in node data type

At the moment, it makes no sense to make the fields of Node components strict as we exploit laziness for lazy marshalling back to Haskell. However, the following would be even better.

  • Make all Node components strict (at least those of elementary type, but maybe also some frequently modified ones of Point, Rect, and so on types).
  • Marshal all strict components eagerly from ObjC to Haskell (in one foreign call!) and keep the rest being marshalled lazily.
  • Set the eagerly marshalled components on the ObjC object only if they changed in the Haskell world (using comparisons on the elementary types, rather than on thunk references).
  • Do the same for Scene. (Maybe even do it first for Scene.)

This balances thunk creation and entering overhead with the overhead of marshalling elementary values that are not ending up being changed.

nodePaused and scenePaused have no effect

I'm playing around with the SpriteKit binding trying to learn and understand the basics. I have the following simple scene:

import Graphics.SpriteKit 

moveThisNode = (moveTo (Point 200 200)) {actionDuration = 10}

aSprite = (spriteWithColorSize redColor (Size 2 2)) {
  nodePosition = Point 125 125,
  nodeSpeed = 1,
  nodeActionDirectives = [
    RunAction moveThisNode Nothing
  ],
  nodePaused = True
}

sceneWithSprite = (sceneWithSize (Size 250 250)) { 
  scenePaused = True,
  sceneChildren = [aSprite]
}

Since both, the node and the scene are set to be paused, I would expect the sprite not to move.

However, when I render the sceneWithSprite in Haskell for Mac, I clearly see the little red sprite moving.

Am I doing something wrong? Is my understanding of paused wrong?

Loading .sks files

  • Provide access to SKNode(fileNamed:)
  • The docs say that the archive needs to be in the app bundle. Can we load it from the .hsproj project resources?
  • Create a new ticket to also load action files.

Support profiling builds

Currently, the build system doesn't build a profiling flavour of spritekit. This is as the unique identifier names generate by language-c-inline for 'hswrapper's get different names in a profiling build, and so, would need a different variant of the wrapper Objective-C code.

This is really an infelicity of language-c-inline.

Mistake in naming of sprite node constructors

  • All the spriteWithXYZ functions actually ought to be called spriteNodeWithXYZ.
  • Rename, but keep the old version (deprecated) around for a while to ease the transition. Maybe we should keep them and introduce similar short forms for the other nodes...
  • Add support for normal maps to sprites and also add light nodes (and the needed functionality on textures). Moved to #12.

Improve SKNode marshalling

Consider this alternative scheme (and adapt the todo items below):

  • The SKNode representation keeps a stable pointer to the Haskell representation it originates from. (Then, we can probably get rid of the special case of how we are doing that for scene nodes right now and we don't need to have the "haskellUserData" entry in SKNodes userData as this is subsumed bu a reference to the entire Haskell representation.)
  • We update the Haskell representation each time we marshal the node back from SKNode to Haskell.
  • When merging a (possibly) updated Haskell representation into the foreign SKNode representation, we always determine the to be updated delta by comparing to the Haskell representation refereed to be the SKNode (that crucial depends on the previous point).
  • When we call back into Haskell in scene update functions or custom actions, we have no idea which part of the referenced Haskell representation (except the user data) is still valid and so need to create a new Haskell representation from scratch. However, when we need to marshal the Haskell representation to an SKNode to invoke methods, such as frame or calculateAccumulatedFrame (assuming nodeForeign is set), we update the reference to the Haskell representation, which then serves as the delta for the next Haskell to SKNode marshalling — saving work when we call multiple functions, such as frame or when the Haskell representation is passed back unchanged as a result node.
    One big advantage of this scheme is that reordered lists of children don't take any effort to match the old and new Haskell representations that need to be compared.
  • The current implementation of addChildren is quite inefficient for large arrays of children. We should use sets for the containment testing when the arrays get bigger.
  • The current implementation of addChildren doesn't generally preserve the order of the children (wrt to the order in the Haskell structure). This is bad as the drawing order depends on the order of the children.
  • Improve Node.addChildren and related functions. When we update an existing SKNode tree with Haskell-side changes, currently, the whole tree gets traversed if the nodeChildren field of the root changed at all. This is quite inefficient on large trees. It would be better, while inspecting the individual children to see whether some children didn't change at all and then prune the traversal using that information.

Building HaskellSpriteKit fails, Type Constructor or class not in scope

I am having a problem compiling the HaskellSprite kit in Xcode 9.1 with GHC v. 8.2.2.
The error is placed below:

Showing Recent Messages
[ 8 of 14] Compiling Graphics.SpriteKit.Types ( Graphics/SpriteKit/Types.hs, /Users/braedenfoster/Library/Developer/Xcode/DerivedData/HaskellSpriteKit-ckqakbbckyvankhijtgdgqnstbxs/Build/Intermediates.noindex/SwiftMigration/HaskellSpriteKit/Intermediates.noindex/HaskellSpriteKit.build/Debug/HaskellSpriteKit.build/spritekit/build/Graphics/SpriteKit/Types.o )


Graphics/SpriteKit/Types.hs:395:19: error:


    Not in scope: type constructor or class ‘GHC.Any’


    Module ‘GHC.Prim’ does not export ‘Any’.


    |


395 | newtype Any = Any GHC.Any


    |                   ^^^^^^^


cabal: Leaving directory 'HaskellSpriteKit/spritekit'


cabal: Error: some packages failed to install:


spritekit-0.9.0.2-CQBFv3qNfz48eVFPtHanfT failed during the building phase. The


exception was:


ExitFailure 1


cabal install failed

Potentially outdated `haskellScenePtr`

The haskellScenePtr refers to the Haskell version of a scene including the scene's children. Those children may, however, be outdated when we get back to sceneUpdate on the next frame as both actions and contact handlers may have changed arbitrary children in arbitrary ways.

  • Update scene children when making use of haskellScenePtr at the start of a frame (using lazy marshalling as usual).

Optimise marshalling of `Action`s

If actions are used repeatedly, it is more efficient to create the action objects once and run them on multiple nodes. To do this in the Haskell binding, use stable names to identify identical actions (actually action trees) and reuse them.

(We need to be careful to free them after a while of not being used, though.)

Normal maps and light nodes

  • Add support for normal maps to sprites (constructors and properties)
  • Add light nodes
  • Add normal map functionality to textures

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.