sdl2-sprite (empty) → 0.0.0
raw patch · 6 files changed
+283/−0 lines, 6 filesdep +basedep +optparse-simpledep +sdl2setup-changed
Dependencies added: base, optparse-simple, sdl2, sdl2-image, sdl2-sprite, split, text
Files
- LICENSE +30/−0
- README.md +71/−0
- Setup.hs +2/−0
- app/Main.hs +73/−0
- sdl2-sprite.cabal +36/−0
- src/SDL/Sprite.hs +71/−0
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Chris Done (c) 2018++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Chris Done nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,71 @@+# sdl2-sprite++Create a horizontal sprite like this:++++The size of the intended result image is 220x130, so provide that:++ sdl2-sprite blue-fish.png 220x130 --fps 15++This will open a window animating that sprite:++++(Note: this is a screenflow recording of the animation manually edited+to repeat, so it's not as smooth as in reality.)++The animation repeats indefinitely at the desired framerate.++## Using Haskell API++Use `load` to load in a sprite for a `Renderer`:++``` haskell+load :: MonadIO m => Renderer -> FilePath -> V2 CInt -> m Sprite+```++To render the sprite at a given position use `render`:++``` haskell+render :: MonadIO m => Sprite -> V2 CInt -> m ()+```++To advance the sprite's frame to the next frame (or loop), use+`animate`:++``` haskell+animate :: Sprite -> Sprite+```++This is a pure function, so you can use it in a loop. See the source+in `app/Main.hs` of this repository for an example.+++## How to make sprites in Inkscape++Choose the size of your intended result e.g. 220x130 and then setup+the grid in Inkscape to show that size (see the Spacing X/Spacing Y+fields):++++It'll look like this, which is handy:++++When you want to make a new frame, use `Ctrl-D` to duplicate the+selection and use the Transform tool to move it exactly 220 pixels to+the right and hit Apply.++++Make sure that your document dimensions are your width x your frame+count.++Finally, export your image with the right "Image size" - make sure the+size is 130 so that the width scales right:++++Now you're good to go! Hit Export.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ app/Main.hs view
@@ -0,0 +1,73 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE ViewPatterns #-}+-- |++module Main where++import Control.Concurrent+import Control.Monad+import Data.List.Split+import Data.Monoid+import qualified Data.Text as T+import Options.Applicative.Simple+import SDL+import qualified SDL.Sprite+import Text.Read++main :: IO ()+main = do+ ((fp, dim, ms), ()) <-+ simpleOptions+ "0.0.0"+ "sdl2-sprite"+ "Animate sprites with sdl2"+ ((,,) <$> argument str (metavar "FILE") <*>+ argument+ (eitherReader+ (\i ->+ case splitOn "x" i of+ [w, h] -> V2 <$> readEither w <*> readEither h+ _ -> Left "invalid WIDTHxHEIGHT specification"))+ (metavar "WIDTHxHEIGHT") <*>+ (fmap+ (\fps -> 1000 / fps)+ (option+ auto+ (long "fps" <> value (15 :: Double) <> help "Frames per second")) <|>+ option+ auto+ (long "delay" <> value (50 :: Double) <>+ help "Delay between frames in ms")))+ empty+ initializeAll+ window <-+ createWindow+ (T.pack fp)+ defaultWindow {windowInitialSize = dim, windowHighDPI = True}+ renderer <- createRenderer window (-1) defaultRenderer+ -- Resize the window (smaller) if in high DPI (retina) mode.+ do Just (Rectangle _ (V2 w h)) <- SDL.get (SDL.rendererViewport renderer)+ let scale =+ let (V2 w0 h0) = dim+ in (V2+ (fromIntegral w / fromIntegral w0 :: Double)+ (fromIntegral h / fromIntegral h0 :: Double))+ windowSize window $= (fmap round (fmap fromIntegral dim / scale))+ -- Load up the sprite.+ sprite0 <- SDL.Sprite.load renderer fp dim+ -- Render the sprite on a timer.+ let loop (!sprite) = do+ events <- pollEvents+ let eventQuit event =+ case eventPayload event of+ KeyboardEvent keyboardEvent ->+ keyboardEventKeyMotion keyboardEvent == Pressed &&+ keysymKeycode (keyboardEventKeysym keyboardEvent) == KeycodeQ+ WindowClosedEvent {} -> True+ _ -> False+ clear renderer+ SDL.Sprite.render sprite (V2 0 0)+ present renderer+ threadDelay (round (1000 * ms))+ unless (any eventQuit events) (loop (SDL.Sprite.animate sprite))+ loop sprite0
+ sdl2-sprite.cabal view
@@ -0,0 +1,36 @@+name: sdl2-sprite+version: 0.0.0+synopsis: Sprite previewer/animator+description: View your sprites in an animated way with SDL2+homepage: https://github.com/chrisdone/sdl2-sprite#readme+license: BSD3+license-file: LICENSE+author: Chris Done+maintainer: chrisdone@gmail.com+copyright: 2015 Chris Done+category: Web+build-type: Simple+extra-source-files: README.md+cabal-version: >=1.10++library+ hs-source-dirs: src+ ghc-options: -Wall+ exposed-modules: SDL.Sprite+ build-depends: base >= 4.7 && < 5,+ sdl2+ , sdl2-image+ default-language: Haskell2010++executable sdl2-sprite+ hs-source-dirs: app+ main-is: Main.hs+ ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N+ build-depends: base+ , sdl2-sprite+ , sdl2+ , sdl2-image+ , optparse-simple+ , text+ , split+ default-language: Haskell2010
+ src/SDL/Sprite.hs view
@@ -0,0 +1,71 @@+-- | Handle sprites in a simple way.++module SDL.Sprite+ ( load+ , animate+ , render+ , Sprite+ ) where++import Control.Exception+import Control.Monad.IO.Class+import Foreign.C.Types+import qualified SDL.Image+import SDL.Vect+import SDL.Video.Renderer++-- | A loaded sprite.+data Sprite = Sprite+ { spriteDimensions :: !(V2 CInt)+ -- ^ Render dimensions.+ , spriteTexture :: !Texture+ -- ^ The texture to render from.+ , spriteFrame :: !CInt+ -- ^ The current frame index.+ , spriteTextureInfo :: !TextureInfo+ -- ^ Info about the sprite texture.+ , spriteRenderer :: !Renderer+ -- ^ The renderer.+ }++-- | Get the current source rectangle of the sprite's frame.+spriteRectangle :: Sprite -> Rectangle CInt+spriteRectangle s = Rectangle (P (V2 x 0)) (V2 w h)+ where (V2 w h) = spriteDimensions s+ x = spriteFrame s * w++-- | Load a sprite from file.+load :: MonadIO m => Renderer -> FilePath -> V2 CInt -> m Sprite+load ren fp dimensions =+ liftIO+ (do texture <- SDL.Image.loadTexture ren fp+ textInfo <- queryTexture texture+ evaluate+ (Sprite+ { spriteDimensions = dimensions+ , spriteTexture = texture+ , spriteFrame = 0+ , spriteTextureInfo = textInfo+ , spriteRenderer = ren+ }))++-- | Advance sprite to the next frame.+animate :: Sprite -> Sprite+animate s =+ s+ { spriteFrame =+ if (spriteFrame s + 1) * w < textureWidth (spriteTextureInfo s)+ then spriteFrame s + 1+ else 0+ }+ where+ (V2 w _) = spriteDimensions s++-- | Render the sprite at the given coordinates.+render :: MonadIO m => Sprite -> V2 CInt -> m ()+render s xy =+ copy+ (spriteRenderer s)+ (spriteTexture s)+ (Just (spriteRectangle s))+ (Just (Rectangle (P xy) (spriteDimensions s)))