Sprite rendering
This commit is contained in:
parent
53d0a5aaf0
commit
a87fe00b52
6 changed files with 181 additions and 11 deletions
80
app/Graphics.hs
Normal file
80
app/Graphics.hs
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
module Graphics (
|
||||||
|
renderFrame
|
||||||
|
) where
|
||||||
|
|
||||||
|
import SDL
|
||||||
|
import SnakeLib
|
||||||
|
import Foreign.C (CInt)
|
||||||
|
import Data.Array (indices)
|
||||||
|
import Control.Monad (void)
|
||||||
|
|
||||||
|
renderFrame :: Renderer -> Texture -> IO ()
|
||||||
|
renderFrame renderer texture = do
|
||||||
|
rendererDrawColor renderer $= V4 32 32 32 255
|
||||||
|
clear renderer
|
||||||
|
rendererDrawColor renderer $= V4 255 255 255 255
|
||||||
|
renderState renderer texture $ initialState (20, 20) (2, 2)
|
||||||
|
present renderer
|
||||||
|
|
||||||
|
renderState :: Renderer -> Texture -> GameState -> IO ()
|
||||||
|
renderState renderer texture state = do
|
||||||
|
void $ traverse (renderTile renderer texture state) (indices $ gameBoard state)
|
||||||
|
|
||||||
|
renderTile :: Renderer -> Texture -> GameState -> Pos -> IO ()
|
||||||
|
renderTile renderer texture state pos = case tileAt (gameBoard state) pos of
|
||||||
|
Empty -> return ()
|
||||||
|
Apple -> renderSpriteAt renderer texture APPLE pos
|
||||||
|
Snake ->
|
||||||
|
let size = gameBoardSize state
|
||||||
|
in renderSpriteAt renderer texture (snakeSprite (neighborTiles size (gameBoard state) pos) (pos == snakeHead state))pos
|
||||||
|
|
||||||
|
snakeSprite :: (Tile, Tile, Tile, Tile) -> Bool -> Sprite
|
||||||
|
snakeSprite (Snake, _, _, _) True = SNAKE_HEAD_DOWN
|
||||||
|
snakeSprite (_, Snake, _, _) True = SNAKE_HEAD_LEFT
|
||||||
|
snakeSprite (_, _, Snake, _) True = SNAKE_HEAD_UP
|
||||||
|
snakeSprite (_, _, _, Snake) True = SNAKE_HEAD_RIGHT
|
||||||
|
snakeSprite (Snake, _, Snake, _) _ = SNAKE_VERTICAL
|
||||||
|
snakeSprite (_, Snake, _, Snake) _ = SNAKE_HORIZONTAL
|
||||||
|
snakeSprite (Snake, Snake, _, _) _ = SNAKE_TURN_UP_RIGHT
|
||||||
|
snakeSprite (_, Snake, Snake, _) _ = SNAKE_TURN_DOWN_RIGHT
|
||||||
|
snakeSprite (_, _, Snake, Snake) _ = SNAKE_TURN_DOWN_LEFT
|
||||||
|
snakeSprite (Snake, _, _, Snake) _ = SNAKE_TURN_UP_LEFT
|
||||||
|
snakeSprite (Snake, _, _, _) _ = SNAKE_TAIL_UP
|
||||||
|
snakeSprite (_, Snake, _, _) _ = SNAKE_TAIL_RIGHT
|
||||||
|
snakeSprite (_, _, Snake, _) _ = SNAKE_TAIL_DOWN
|
||||||
|
snakeSprite (_, _, _, Snake) _ = SNAKE_TAIL_LEFT
|
||||||
|
snakeSprite _ _ = APPLE
|
||||||
|
|
||||||
|
renderSpriteAt :: Renderer -> Texture -> Sprite -> (Int, Int) -> IO ()
|
||||||
|
renderSpriteAt renderer texture sprite (x, y) = do
|
||||||
|
let srcRect = getSpriteSheetLocation $ getSpriteIndex sprite
|
||||||
|
let dstRect = Rectangle (P (V2 (64 * (fromIntegral x)) (64 * (fromIntegral y)))) (V2 64 64)
|
||||||
|
copy renderer texture (Just srcRect) (Just dstRect)
|
||||||
|
|
||||||
|
getSpriteSheetLocation :: (Int, Int) -> Rectangle CInt
|
||||||
|
getSpriteSheetLocation (y, x) = Rectangle (P (V2 (16 * (fromIntegral x)) (16 * (fromIntegral y)))) (V2 16 16)
|
||||||
|
|
||||||
|
getSpriteIndex :: Sprite -> (Int, Int)
|
||||||
|
getSpriteIndex SNAKE_HEAD_DOWN = (0, 0)
|
||||||
|
getSpriteIndex SNAKE_HEAD_LEFT = (0, 1)
|
||||||
|
getSpriteIndex SNAKE_HEAD_UP = (0, 2)
|
||||||
|
getSpriteIndex SNAKE_HEAD_RIGHT = (0, 3)
|
||||||
|
getSpriteIndex SNAKE_TURN_UP_RIGHT = (1, 0)
|
||||||
|
getSpriteIndex SNAKE_TURN_DOWN_RIGHT = (1, 1)
|
||||||
|
getSpriteIndex SNAKE_TURN_DOWN_LEFT = (1, 2)
|
||||||
|
getSpriteIndex SNAKE_TURN_UP_LEFT = (1, 3)
|
||||||
|
getSpriteIndex SNAKE_VERTICAL = (2, 0)
|
||||||
|
getSpriteIndex SNAKE_HORIZONTAL = (2, 1)
|
||||||
|
getSpriteIndex SNAKE_TAIL_UP = (3, 0)
|
||||||
|
getSpriteIndex SNAKE_TAIL_RIGHT = (3, 1)
|
||||||
|
getSpriteIndex SNAKE_TAIL_DOWN = (3, 2)
|
||||||
|
getSpriteIndex SNAKE_TAIL_LEFT = (3, 3)
|
||||||
|
getSpriteIndex APPLE = (2, 2)
|
||||||
|
|
||||||
|
data Sprite =
|
||||||
|
SNAKE_HEAD_DOWN | SNAKE_HEAD_LEFT | SNAKE_HEAD_UP | SNAKE_HEAD_RIGHT |
|
||||||
|
SNAKE_TURN_UP_RIGHT | SNAKE_TURN_DOWN_RIGHT | SNAKE_TURN_DOWN_LEFT | SNAKE_TURN_UP_LEFT |
|
||||||
|
SNAKE_VERTICAL | SNAKE_HORIZONTAL |
|
||||||
|
SNAKE_TAIL_UP | SNAKE_TAIL_RIGHT | SNAKE_TAIL_DOWN | SNAKE_TAIL_LEFT |
|
||||||
|
APPLE
|
||||||
|
|
||||||
46
app/Main.hs
46
app/Main.hs
|
|
@ -1,8 +1,48 @@
|
||||||
module Main where
|
module Main where
|
||||||
|
|
||||||
import qualified MyLib (someFunc)
|
import SDL
|
||||||
|
import qualified SDL.Image as IMG
|
||||||
|
import qualified Data.Text as Text
|
||||||
|
import Control.Monad (unless)
|
||||||
|
import Graphics
|
||||||
|
import SnakeLib
|
||||||
|
import SDL.Raw (getTicks)
|
||||||
|
import Data.Word (Word32)
|
||||||
|
|
||||||
main :: IO ()
|
main :: IO ()
|
||||||
main = do
|
main = do
|
||||||
putStrLn "Hello, Haskell!"
|
initializeAll
|
||||||
MyLib.someFunc
|
window <- createWindow (Text.pack "Hello, World") defaultWindow
|
||||||
|
renderer <- createRenderer window (-1) defaultRenderer
|
||||||
|
|
||||||
|
texture <- IMG.loadTexture renderer "assets/spritesheet.png"
|
||||||
|
|
||||||
|
appLoop renderer texture
|
||||||
|
|
||||||
|
destroyTexture texture
|
||||||
|
destroyRenderer renderer
|
||||||
|
destroyWindow window
|
||||||
|
|
||||||
|
targetFps :: Word32
|
||||||
|
targetFps = 60
|
||||||
|
|
||||||
|
targetFrameMs :: Word32
|
||||||
|
targetFrameMs = 1000 `div` targetFps
|
||||||
|
|
||||||
|
appLoop :: Renderer -> Texture -> IO ()
|
||||||
|
appLoop renderer texture = do
|
||||||
|
frameStart <- getTicks
|
||||||
|
events <- pollEvents
|
||||||
|
let eventIsExitPress event = case eventPayload event of
|
||||||
|
KeyboardEvent keyboardEvent ->
|
||||||
|
keyboardEventKeyMotion keyboardEvent == Pressed &&
|
||||||
|
keysymKeycode (keyboardEventKeysym keyboardEvent) == KeycodeQ
|
||||||
|
WindowClosedEvent _ -> True
|
||||||
|
_ -> False
|
||||||
|
exitPressed = any eventIsExitPress events
|
||||||
|
renderFrame renderer texture
|
||||||
|
frameEnd <- getTicks
|
||||||
|
let elapsed = frameEnd - frameStart
|
||||||
|
delay (targetFrameMs - elapsed)
|
||||||
|
unless exitPressed (appLoop renderer texture)
|
||||||
|
|
||||||
|
|
|
||||||
BIN
assets/spritesheet.png
Normal file
BIN
assets/spritesheet.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1 KiB |
|
|
@ -58,7 +58,7 @@ library
|
||||||
import: warnings
|
import: warnings
|
||||||
|
|
||||||
-- Modules exported by the library.
|
-- Modules exported by the library.
|
||||||
exposed-modules: MyLib
|
exposed-modules: SnakeLib
|
||||||
|
|
||||||
-- Modules included in this library but not exported.
|
-- Modules included in this library but not exported.
|
||||||
-- other-modules:
|
-- other-modules:
|
||||||
|
|
@ -67,7 +67,9 @@ library
|
||||||
-- other-extensions:
|
-- other-extensions:
|
||||||
|
|
||||||
-- Other library packages from which modules are imported.
|
-- Other library packages from which modules are imported.
|
||||||
build-depends: base ^>=4.20.2.0
|
build-depends:
|
||||||
|
base ^>=4.20.2.0,
|
||||||
|
array
|
||||||
|
|
||||||
-- Directories containing source files.
|
-- Directories containing source files.
|
||||||
hs-source-dirs: src
|
hs-source-dirs: src
|
||||||
|
|
@ -83,7 +85,8 @@ executable snake-hs
|
||||||
main-is: Main.hs
|
main-is: Main.hs
|
||||||
|
|
||||||
-- Modules included in this executable, other than Main.
|
-- Modules included in this executable, other than Main.
|
||||||
-- other-modules:
|
other-modules:
|
||||||
|
Graphics
|
||||||
|
|
||||||
-- LANGUAGE extensions used by modules in this package.
|
-- LANGUAGE extensions used by modules in this package.
|
||||||
-- other-extensions:
|
-- other-extensions:
|
||||||
|
|
@ -91,7 +94,11 @@ executable snake-hs
|
||||||
-- Other library packages from which modules are imported.
|
-- Other library packages from which modules are imported.
|
||||||
build-depends:
|
build-depends:
|
||||||
base ^>=4.20.2.0,
|
base ^>=4.20.2.0,
|
||||||
snake-hs
|
snake-hs,
|
||||||
|
text,
|
||||||
|
array,
|
||||||
|
sdl2,
|
||||||
|
sdl2-image
|
||||||
|
|
||||||
-- Directories containing source files.
|
-- Directories containing source files.
|
||||||
hs-source-dirs: app
|
hs-source-dirs: app
|
||||||
|
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
module MyLib (someFunc) where
|
|
||||||
|
|
||||||
someFunc :: IO ()
|
|
||||||
someFunc = putStrLn "someFunc"
|
|
||||||
47
src/SnakeLib.hs
Normal file
47
src/SnakeLib.hs
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
module SnakeLib (
|
||||||
|
MovementInput (..),
|
||||||
|
Tile (..),
|
||||||
|
Pos,
|
||||||
|
Board,
|
||||||
|
tileAt,
|
||||||
|
wrapPos,
|
||||||
|
neighborTiles,
|
||||||
|
|
||||||
|
GameState (..),
|
||||||
|
initialState
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Data.Array
|
||||||
|
|
||||||
|
data MovementInput = UpPressed | RightPressed | DownPressed | LeftPressed | NothingPressed
|
||||||
|
|
||||||
|
data Tile = Snake | Apple | Empty
|
||||||
|
|
||||||
|
type Pos = (Int, Int)
|
||||||
|
type Board = Array Pos Tile
|
||||||
|
|
||||||
|
tileAt :: Board -> Pos -> Tile
|
||||||
|
tileAt board (x, y) = (board ! (x, y))
|
||||||
|
|
||||||
|
wrapPos :: (Int, Int) -> Pos -> Pos
|
||||||
|
wrapPos (w, h) (x, y) =
|
||||||
|
(x `mod` w, y `mod` h)
|
||||||
|
|
||||||
|
neighborTiles :: (Int, Int) -> Board -> Pos -> (Tile, Tile, Tile, Tile)
|
||||||
|
neighborTiles size board (x, y) =
|
||||||
|
( tileAt board (wrapPos size (x, y - 1)) -- up
|
||||||
|
, tileAt board (wrapPos size (x + 1, y)) -- right
|
||||||
|
, tileAt board (wrapPos size (x, y + 1)) -- down
|
||||||
|
, tileAt board (wrapPos size (x - 1, y)) -- left
|
||||||
|
)
|
||||||
|
|
||||||
|
data GameState = GameState {gameBoard :: Board, gameBoardSize :: (Int, Int), snakeHead :: Pos}
|
||||||
|
|
||||||
|
emptyBoard :: (Int, Int) -> Array (Int, Int) Tile
|
||||||
|
emptyBoard (w, h) = array ((0, 0), (w-1, h-1)) [((i, j), Empty) | i <- [0..w-1], j <- [0..h-1]]
|
||||||
|
|
||||||
|
initialState :: (Int, Int) -> (Int, Int) -> GameState
|
||||||
|
initialState size (x, y) = GameState {gameBoard=(emptyBoard size) // [((x-1, y), Snake), ((x, y), Snake)], gameBoardSize=size, snakeHead=(x, y)}
|
||||||
|
|
||||||
|
gameTick :: GameState -> MovementInput -> GameState
|
||||||
|
gameTick = undefined
|
||||||
Loading…
Add table
Add a link
Reference in a new issue