Welcome, Guest. Please login or register.

Login with username, password and session length

 
Advanced search

1411273 Posts in 69323 Topics- by 58380 Members - Latest Member: bob1029

March 28, 2024, 01:14:43 AM

Need hosting? Check out Digital Ocean
(more details in this thread)
TIGSource ForumsCommunityDevLogsI am making an rpg with Libgdx
Pages: 1 [2] 3
Print
Author Topic: I am making an rpg with Libgdx  (Read 8349 times)
Yxven
Level 0
***



View Profile
« Reply #20 on: October 14, 2014, 08:06:12 AM »

I've been trying to manage my game state with the state monad, so that I can write functions like this:
Code:
createEntity :: World Entity
createEntity = do
    gameState <- get
    let serial = entitySerial gameState
    let entity = Entity serial $ componentsFromList []
    let entities' = Map.insert serial entity $ entities gameState
    put gameState {entitySerial = serial + 1, entities = entities'}
    return entity
that update my game state while returning the particular part of the world I want for further processing.
The problem is eventually, I have to turn it back into World GameState GameState to pass it back into my
Gloss game loop, so I keep trying to write a function like this:

Code:
makeWorldState :: World a -> World GameState
makeWorldState worldEntity = return gameState
    where
    gameState = execState (get) worldEntity;

The trouble is I can't convert State monads like that. I could write my own monad to be able to do that, but my createEntity function isn't taking advantage of the way monads can pipe data to each other and is not particularly better than using normal functions. Hence, I'm abandoning the state monad approach.
Logged
Yxven
Level 0
***



View Profile
« Reply #21 on: October 14, 2014, 08:28:08 PM »

I am finally making progress again. I have my game generating the map. You'll have to imagine it, because I can't display it yet. Pretend like you're in math class, and picture the Cartesian coordinate system.

I was trying to get them to render today, but I got thrown off by my game loop.

Code:
playSource
:: forall world .
=> Display
Display mode.

-> Color
Background color.

-> Int
Number of simulation steps to take for each second of real time.

-> world
The initial world.

-> (world -> Picture)
A function to convert the world a picture.

-> (Event -> world -> world)
A function to handle input events.

-> (Float -> world -> world)
A function to step the world one iteration. It is passed the period of time (in seconds) needing to be advanced.
-> IO ()

It looks simple enough. It's just display, input, update, but if you look closer at my draw function (world -> Picture), you'll notice that there is no built-in way to save state on the display side. If I want to remember which frame of the animation my hero is on, I'd have to somehow divine that from the game state (without knowing the timeDelta). I spent a while trying to figure out how to do this, but eventually I just decided to pervert the game update function.

Code:
update :: Float -> GameState -> GameState
update tick = updateGraphics tick . updateGame tick

I'll just have to store all of my state within GameState. I've considered renaming it "global." This is what it currently looks like.

Code:
data GameState = GameState {entitySerial :: Int, entities :: (Map.Map Serial Entity), positionState :: PositionState, randomState :: StdGen}

If it looks like a random assortment of data, we are on the same page. Eventually, this data structure is going to expand until it consumes Tokyo. I don't see any other way how to do it. One object has to contain all of my data, so the best I can do is organize it better.

My entity component system on the other hand is looking quite svelt.

Code:
import qualified Data.Set as Set
import StringTable.Atom

data Component = Component
    Float                             --Priority
    Atom                            --Name

componentFoldl :: (a -> Component -> a) -> a -> Components -> a
componentFoldl = Set.foldl'

componentInsert :: Component -> Components -> Components
componentInsert component components = Set.insert component components

componentsToList :: Components -> [Component]
componentsToList = Set.toAscList

instance Eq Component where
    (Component priority1 name1) == (Component priority2 name2) = priority1 == priority2 && name1 == name2

instance Ord Component where
    compare (Component priority1 name1) (Component priority2 name2)
        | priority1 > priority2 = GT
        | priority1 < priority2 = LT
        | name1 > name2 = GT
        | name1 < name2 = LT
        | otherwise = EQ

type Components = Set.Set Component

emptyComponents :: Components
emptyComponents = Set.empty

data Entity = Entity Serial Components

type Serial = Int

My components don't actually store any data, so they function more like a lookup table for whether an entity has that ability. The actual state is stored in GameState dictionaries accessible by the entity's unique serial number. This is for maximum flexibility. I'm trying to design my game, so that it can become a game framework for other Haskell newbies (not that I'll ever finish it).
Logged
Armageddon
Level 6
*



View Profile
« Reply #22 on: October 14, 2014, 08:41:29 PM »

I was expecting a roguelike where you like, play as a valley girl and like like.
Logged

Yxven
Level 0
***



View Profile
« Reply #23 on: October 17, 2014, 01:39:45 PM »

Well, I've been working on getting my draw function drawing. I finally got everything compiling today, but performance is abysmal.

I tried getting the profiler to work which is a huge hassle and involves reinstalling every library, but I never got it to produce any output. I'm 99% sure my performance problem is caused by the naïve way I'm calculating which objects should be displayed, but it's disappointing not being able prove that.

My options are to:
  • Implement this low level camera functionality that should be a part of any decent game framework (knowing that I'll also have to implement other low-level shit later).
  • Simplify my game design to be grid based.
  • Sacrifice Gloss for sdl2.
  • Quit and do something else.


None of these options are particularly appealing.

Logged
Andy Rhetoric
Level 0
**



View Profile
« Reply #24 on: October 17, 2014, 02:04:06 PM »

Really entertaining devlog. 10/10. Would read again.
Logged
Yxven
Level 0
***



View Profile
« Reply #25 on: October 21, 2014, 03:43:14 PM »

The good news is I figured out how to use Haskell's profiler.
The bad news is Haskell profiling bugs out when profiling *my* game.

What does work for me is the memory profiling (although, that too tried to sabotage me today, but I binary searched my project and found the commit that broke that).

In any case, I found the memory leak that seemed to have been causing most of my performance problems.

Before:


After:


It was satisfying to realize that the mistake was due to my incompetence rather than my incompetence with Haskell's laziness.

Also, I have drawing working:


There is no movement yet. My project is more about art than gameplay right now.

I'm using the DawnLike tile set because no one wants to look at my stick figures.
http://opengameart.org/content/dawnlike-16x16-universal-rogue-like-tileset-v18

Also, I open sourced my project, so you can look at my code and Facepalm
https://github.com/Yxven/roguelike
Logged
Yxven
Level 0
***



View Profile
« Reply #26 on: October 23, 2014, 06:22:37 PM »

Haskell IO really feels like a zombie apocalypse in your code base. I had it beaten back all the way to my main function, but then I added a physics engine and the zombies took over.

On the bright side, my player would be colliding with trees if he could move.
Logged
Yxven
Level 0
***



View Profile
« Reply #27 on: October 25, 2014, 03:40:54 PM »

Well, I have movement working.. sort of.



I figured my code base was well-rounded enough that I could get feedback on things I could improve. The only feedback I've received so far is that my problems are caused by a poor understanding of Haskell, and that I should separate my transformations and reductions with yonedas which is apparently an advanced concept that is built on top of the other advanced concepts I have been procrastinating.
Logged
agersant
Level 4
****



View Profile
« Reply #28 on: October 26, 2014, 03:07:10 PM »

Very entertaining devlog. Reminds me of times when I first started using boost libraries (esp boost::spirit).
Logged
Yxven
Level 0
***



View Profile
« Reply #29 on: November 02, 2014, 08:16:44 PM »

I don't particularly like hearing that my code is shitty, so I been spending my time trying to learn advanced Haskell features. I've unfortunately made little progress doing this.

Haskell is pretty easy to learn until you get to the point where it no longer maps easily to traditional programming languages, and then, not only does it become harder for someone with a traditional programming background to learn but the quantity and quality of learning resources also drops dramatically. Books and college courses largely stop once they introduce monads, so you are left with scattered blog posts and poorly written documentation.

I've been at this for a month now, and I don't have much to show for it. For comparison's sake, last month, I wrote a top-down multiplayer death match schmup with animations, particle effects, an authoritative server, and 36 different spells in unity without ever having built anything with unity or c# before. That beats the hell out of loading some sprites and being able to wander around a static screen knocking around trees.

That said, it's going to be painful to return to a language without static inferred types. That is a killer feature. Currying functions is nice too, but I'm still on the fence about Haskell's other unique features: laziness, separating IO, and enforced immutability.
Logged
Yxven
Level 0
***



View Profile
« Reply #30 on: November 07, 2014, 10:56:52 PM »

Iceland_jack in #Haskell-beginners (on Freenet) showed me how to use the state monad correctly and pointed me towards monad transformers. This really helped me clean up my code base.

Before I had a bunch of crap code like this:
Code:
createPlayer :: Position -> GameState -> IO GameState
createPlayer position gameState = do
    let (entity, gameState') = createEntity gameState player
    let square = createSquare (12 * float2Double normalScale) (16 * float2Double normalScale)
    (entity', gameState'') <- addPhysics 1 1 square gameState' entity
    (entity'', gameState3) <- addTransform position 4 4 gameState'' entity'
    let (entity3, gameState4) = addSimpleMovement gameState3 entity''
    let gameState5 = gameState4{playerSerial = getSerial entity3}
    return $ snd $ addRenderable gameState5 entity3

But now it's like this:
Code:
createPlayer :: Position -> GameState Entity
createPlayer position = do
    entity <- createEntity player
    let square = createSquare (12 * float2Double normalScale) (16 * float2Double normalScale)
    entity2 <- addPhysics 1 1 square entity
    entity3 <- addTransform position 4 4 entity2
    entity4 <- addSimpleMovement entity3
    gameData <- get
    put gameData{playerSerial = getSerial entity4}
    entity5 <- addRenderable entity4 renderPlayer
    return entity5

Next, I'm going to make entities consist only of an integer identifier, so I can erase the rest of that "entity3 <-" crap.
Logged
Yxven
Level 0
***



View Profile
« Reply #31 on: November 27, 2014, 11:28:23 AM »

This project is still limping forward. I wasn't quite happy with the way I implemented entities and components. It was extendable and easy to work with, but the goal was to abuse cache locality which my implementation surely missed.

I started rewriting my game so that it would use a entity component system like this: http://www.gamedev.net/page/resources/_/technical/game-programming/implementing-component-entity-systems-r3382 but it seemed like for that to work it would need mutable vectors. Mutable vectors are well supported in Haskell, but you sacrifice laziness and immutability which are two of the main things that make Haskell different.

It's hard for me to walk into a beautiful building, be impressed with the architecture, and the people who built it; and go, "these two support pillars you built your world around are poor choices." I admit I don't really get laziness or immutability, so I asked around and one of the benefits is easier parallelism.

In theory, all you need to do to parallelize your program is embrace laziness, queue up a lot of work like you would normally program, tell it to do that work in parallel, and then tell it to fully evaluate that work.

Here's code without parallelism (f could be any function)
Code:
  runEval $ do
     a <- (f x)
     b <- (f y)
     return (a,b)

Here's code that calculates a and b on separate processors
Code:
  runEval $ do
     a <- rpar (f x)
     b <- rpar (f y)
     return (a,b)

In practice, Haskell's parallelism (or at least this way of Haskell's parallelism) is more difficult than that because it's not completely obvious when work actually gets evaluated and when it doesn't (in the worst case the garbage collector will collect work you wanted done before it ever got used).

In any case, I'm trying to learn Haskell not a slightly better C, so I decided to try to make my program parallel. The biggest offender in my code base is the C physics engine I'm using. Any code that interacts with that cannot be parallelized with Haskell (to my knowledge), so I've been trying to learn how to write basic collision detection (since I don't actually care about it being physically accurate).

Collision detection is a lot easier than I thought it was. You basically just wrap every collidable object in rectangles that all have the same rotation (axis aligned bounding boxes aka AABBs), and then compare them with a simple boolean test that checks for collisions. To limit the amount of comparisons you make, you usually group close objects together in another AABB and continue wrapping larger groups until you have some sort of tree.

What makes it less simple is that if you construct your tree poorly, you won't actually improve your performance. The simplest way that I can think of to construct the tree would be to first put all of your collidable objects into a multidimensional array that roughly correlates with their x and y coordinates.

Haskell has a library for multidimensional arrays that even supports parallelism. http://hackage.haskell.org/package/repa-3.3.1.2/docs/Data-Array-Repa.html I thought this was great news until I learned that I have to load it with a list or vector. If I have to figure out how to store a three-dimensional array in one dimension, I might as well keep it there.

In any case, I'm spending all of my time trying to figure out the best way to do things and not coding.
Logged
Yxven
Level 0
***



View Profile
« Reply #32 on: November 29, 2014, 08:56:58 PM »

I mass nuked my project since I wanted to redo almost everything.

I have it loading tiles again, and it's using the open source map editor Tiled (which I highly recommend).

Logged
Donutttt
Level 0
**



View Profile
« Reply #33 on: November 30, 2014, 02:19:35 AM »

Wow, a rogue like in haskell... That takes some bravery. I certainly admire your positivity. You seem pretty determined so I have a good feeling that this will become something amazing.
Logged
Yxven
Level 0
***



View Profile
« Reply #34 on: November 30, 2014, 10:54:31 AM »

Wow, a rogue like in haskell... That takes some bravery. I certainly admire your positivity. You seem pretty determined so I have a good feeling that this will become something amazing.

I appreciate the kind words. Although, I fear my game idea has drifted from being a roguelike. Scope creep has seduced me, and I have to sacrifice procedurally generated content to maintain hope of completion.
Logged
Donutttt
Level 0
**



View Profile
« Reply #35 on: November 30, 2014, 05:04:21 PM »

Wow, a rogue like in haskell... That takes some bravery. I certainly admire your positivity. You seem pretty determined so I have a good feeling that this will become something amazing.

I appreciate the kind words. Although, I fear my game idea has drifted from being a roguelike. Scope creep has seduced me, and I have to sacrifice procedurally generated content to maintain hope of completion.

I think it's ok to have feature creep sometimes, as long as you occasionally allow some features to get taken out ha
Logged
The Translocator
Level 2
**


View Profile
« Reply #36 on: November 30, 2014, 07:33:04 PM »

I want to play an RPG where all objects can be pushed around like the trees now. Basically the gameplay would be figuring out how to push the enemies off of cliffs, moving NPCs so that cutscenes don't happen correctly, hiding items, etc. I'm sure it'd be messed up.
Logged

Yxven
Level 0
***



View Profile
« Reply #37 on: December 12, 2014, 07:31:37 PM »

I have to admit - I have poor judgement. I knew that making a physics engine would be a costly mistake, but I chose to attempt it anyway despite already having someone else's engine working.

In any case, it's mostly written now. I just need to glue together the pieces, and pray to the compiler gods that it will all type check before my attention span wanes. Oh, and it'd be nice if it worked off the bat. I'd hate to waste another 4 weeks debugging it.

Theoretically, it will be fast, parallelizable, and support polygons, pin joints, and triggers. What's that you ask?

"Why polygons and pin joints?"
"I thought you were making everything a circle?"

I don't know. It seemed like a good idea at the time.
Logged
dancing_dead
Level 2
**


View Profile
« Reply #38 on: December 13, 2014, 02:50:49 AM »

great devlog. love seeing the bravery going in, and hilarity coming out. haskell is a pretty scary beast to any oo versed programmer, will follow this!
Logged
Yxven
Level 0
***



View Profile
« Reply #39 on: December 18, 2014, 04:15:59 PM »

My physics engine is implemented and glued together now. I wussed out in regards to semi-realistic collision responses because linear algebra is scary, but since I'm not building a physics platformer, that was feature creep anyway (which means it'll probably get implemented sooner or later).

In any case, I want to see my buggy unrealistic physics in action, so I have to get dudes on the screen again.
Logged
Pages: 1 [2] 3
Print
Jump to:  

Theme orange-lt created by panic