Skip to content
Permalink
Browse files

Finished Entity System

- Entities are now a common thread to connect digital data into the virtual world
- Update and render functions are bound to every entity, and are the only ways in which the data changes
- More work is required to change the update and render functions but right now things work okay, more later
- Guy is an example of an entity, he has extra functionality
- He doesnt move right now so it's hard to test, but every frame his velocity adds to his position
- The update function takes a Guy and a CDouble. We already know the Guy, and so he is curried in the function
- This way, we are saving data to the function allowing for something like an entity-component system
  • Loading branch information...
Ashe committed Mar 31, 2018
1 parent 42ef9fe commit 7bcb2a0114fba1e2b0a491e3137371a1bdb6a1dc
Showing with 92 additions and 79 deletions.
  1. +0 −22 src/Entity.hs
  2. +27 −9 src/GameState.hs
  3. +21 −23 src/Guy.hs
  4. +44 −25 src/Main.hs

This file was deleted.

@@ -1,17 +1,23 @@
module GameState
( GameState(State)
, Entity(Entity)
, Options(Options)
, KeyBindings
, initialState
, entities
, options
, deltaTime
, elapsedTime
, updateGameState
, renderGameState
, initialOptions
, screenRes
, frameLimit
, keybindings
, processInput
, eID
, update
, render
) where

import Control.Monad
@@ -20,13 +26,21 @@ import SDL
import qualified SDL

import InputModule
import Entity

-- Hands the current state of the game to various functions
data GameState =
State
{ options :: Options
, entities :: [Entity]
{ options :: Options
, deltaTime :: CDouble
, elapsedTime :: CDouble
, entities :: [Entity]
}

data Entity =
Entity
{ eID :: Int
, update :: GameState -> Entity
, render :: SDL.Renderer -> IO ()
}

-- We will need this later so just making a newtype for now
@@ -38,7 +52,7 @@ data Options =
}

initialState :: GameState
initialState = State initialOptions []
initialState = State initialOptions 0 0 []

initialOptions :: Options
initialOptions =
@@ -50,7 +64,7 @@ initialOptions =

-- Change the gamestate with input
processInput :: GameState -> SDL.EventPayload -> GameState
processInput state@(State (Options _ _ kbs) _) input = exec $ join func
processInput state@(State (Options _ _ kbs) _ _ _) input = exec $ join func
where func = getBoundInput kbs <$> processEvent input
exec (Just f) = f state
exec _ = state
@@ -63,9 +77,13 @@ processEvent _ = Nothing

-- Updates the game state's entities
updateGameState :: GameState -> CDouble -> GameState
updateGameState state delta = state { entities = map (\(Entity _ up _) -> up delta) (entities state)}
updateGameState state delta =
state
{ deltaTime = delta
, elapsedTime = elapsedTime state + delta
, entities = map (\(Entity _ up _) -> up state) (entities state)
}

-- Renders the game state's entities
renderGameState :: GameState -> IO [()]
renderGameState state = sequence $ map (\(Entity _ _ rend) -> rend) $ entities state
-- SDL.copy renderer player (getCurrentFrame newAnimState) $ Just $ SDL.Rectangle (truncate <$> position (entities newState)) (V2 100 100)
renderGameState :: GameState -> SDL.Renderer -> IO [()]
renderGameState state renderer = sequence $ map (\(Entity _ _ rend) -> rend renderer) $ entities state
@@ -4,7 +4,7 @@ module Guy
( Guy(Guy)
, position
, velocity
, tag
, texture
, animation
, guy
, updateGuy
@@ -15,43 +15,41 @@ import Foreign.C.Types
import SDL.Vect
import qualified SDL

import Entity
import GameState
import SDLAnimations

-- The fundamental structures of all our objects in the game
data Guy =
Guy
{ position :: V2 CDouble
{ position :: Point V2 CDouble
, velocity :: V2 CDouble
, tag :: String
, texture :: SDL.Texture
, animation :: AnimationState
} deriving (Show)
}

guy :: Guy -> Int -> Entity
guy g i =
Entity
{ eID = i
, update = flip guy i . updateGuy g
, render = print (i)
, render = renderGuy g
}

updateGuy :: Guy -> CDouble -> Guy
updateGuy guy dt =
guy
updateGuy :: Guy -> GameState -> Guy
updateGuy g st =
g
{ position =
let (V2 newPosX newPosY) = position guy + velocity guy * V2 dt dt
in V2 newPosX newPosY
, animation = updateAnimationState dt 0.1 (animation guy)
let (P pos) = position g
res = screenRes $ options $ st
(V2 newPosX newPosY) = (pos + velocity g) * V2 dt dt
fixedX = max 0 $ min newPosX (fromIntegral (fst res) - 50)
fixedY = max 0 $ min (fromIntegral (snd res) - 100) newPosY
in P $ V2 fixedX fixedY
, animation = updateAnimationState dt 0.1 (animation g)
}
where dt = deltaTime st

-- -- Our initial guy starts out with him roughly in the middle
-- initialGuy :: (CDouble, CDouble) -> Entity
-- initialGuy pos =
-- Entity
-- { position = P $ V2 (fst pos) (snd pos)
-- , velocity = V2 0 0
-- , tag = "male"
-- , animation = "idle"
-- , frame = 0
-- }

renderGuy :: Guy -> SDL.Renderer -> IO ()
renderGuy g renderer =
SDL.copy renderer (texture g) (getCurrentFrame (animation g)) $ Just $ SDL.Rectangle (truncate <$> position g) (V2 100 100)
-- SDL.copy renderer player (getCurrentFrame newAnimState) $ Just $ SDL.Rectangle (truncate <$> position (entities newState)) (V2 100 100)
@@ -43,6 +43,19 @@ main = do
-- Initialise SDL
SDL.initialize [SDL.InitVideo]

-- Create a window with the correct screensize and make it appear
window <- SDL.createWindow "FirstGameHS" SDL.defaultWindow

-- Create a renderer for the window for rendering textures
renderer <-
SDL.createRenderer
window
(-1)
SDL.RendererConfig
{ SDL.rendererType = SDL.AcceleratedRenderer
, SDL.rendererTargetTexture = False
}

-- Set up the first state
-- let jump p@(Guy _ curVel _ _) = p { velocity = curVel * V2 1 0 + jumpVelocity }
-- fall p@(Guy _ curVel _ _) = p { velocity = curVel - jumpVelocity }
@@ -60,40 +73,43 @@ main = do
-- ] blankKeyBindings }
-- state = initialState { options = initOptions }

let state = initialState

-- Create a window with the correct screensize and make it appear
window <- SDL.createWindow "FirstGameHS"
SDL.defaultWindow { SDL.windowInitialSize = uncurry V2 (screenRes (options state)) }
SDL.showWindow window

-- Create a renderer for the window for rendering textures
renderer <-
SDL.createRenderer
window
(-1)
SDL.RendererConfig
{ SDL.rendererType = SDL.AcceleratedRenderer
, SDL.rendererTargetTexture = False
}

SDL.rendererDrawColor renderer $= V4 maxBound maxBound maxBound maxBound

texture <- getTextureFromImg renderer "Assets/foo.bmp"
player <- getTextureFromImg renderer "Assets/rogue.png"
-- Load in the background
background <- getTextureFromImg renderer "Assets/foo.bmp"

-- Load in the player's texture and animations
animsList <- loadAnimations "Assets/rogue.json"
playerTexture <- getTextureFromImg renderer "Assets/rogue.png"
let animationSet = getAnimationSet "rogue" "male" =<< animsList
animation = getAnimation "walk" =<< animationSet
initAnimationState =
AnimationState animationSet animation [] "idle" 0 0

let loop lastTicks state = do
-- Create the player and add it to the entitylist
player =
Guy
{ position = P $ V2 0 0
, velocity = V2 0 0
, texture = playerTexture
, animation = initAnimationState
}

-- Create the initial state and put the player in
let state = initialState { entities = [guy player 0]}

-- Set the window size
SDL.windowSize window $= uncurry V2 (screenRes (options state))

-- Show the window
SDL.showWindow window

-- Main game loop
let loop state previousTicks = do

ticks <- SDL.getTicks
events <- SDL.pollEvents

let delta = 0.001 * (fromIntegral ticks - fromIntegral lastTicks)
let delta = 0.001 * (fromIntegral ticks - fromIntegral previousTicks)
payloads = map SDL.eventPayload events
quit = SDL.QuitEvent `elem` payloads

@@ -102,17 +118,20 @@ main = do
newState = updateGameState worldAfterInput delta

-- Render functions (Background and player)
SDL.copy renderer texture Nothing Nothing
SDL.copy renderer background Nothing Nothing

-- Render all entities
renderGameState newState renderer

-- Delay time until next frame to save processing power
let frameDelay = 1000 / fromIntegral (frameLimit (options newState))
when (delta < frameDelay) $ SDL.delay (truncate $ frameDelay - delta)

SDL.present renderer
unless quit $ loop ticks newState
unless quit $ loop newState ticks

ticks <- SDL.getTicks
loop ticks state
loop state ticks

SDL.destroyWindow window
SDL.quit

0 comments on commit 7bcb2a0

Please sign in to comment.
You can’t perform that action at this time.