Welcome, Guest. Please login or register.

Login with username, password and session length

 
Advanced search

1411504 Posts in 69373 Topics- by 58429 Members - Latest Member: Alternalo

April 25, 2024, 04:16:18 PM

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 8437 times)
Yxven
Level 0
***



View Profile
« on: September 30, 2014, 01:33:57 PM »

I admire developers who are capable of taking an idea and painstakingly develop and blog about it until it becomes Dwarf Fortress. I am not one of those programmers. Our time together will be quick and dirty. Only if I particularly enjoyed myself will I tell you about my future conquests.

What's that you say? "Yaddy yada yada - tell me about your game."

I beg your pardon Señor. I do not so easily share my secrets. I will only tell you about the past for even I cannot predict my fickle heart.

I am writing my game in Haskell. I chose this because Haskell programmers sound smart, and I want to sound smart to. Being new to Haskell and functional programming, I spent my weekend researching Haskell game development. My goal was to find a framework that will hold my hand and change my diaper when I have an accident, but I found no such project (or volunteers).

I selected some libraries, but since I don't currently have any code written, I'm not going to tell you about them yet. However, I will tell you what I decided against using.

Helm - this is supposed to be the Elm of Haskell, and I like where this project is going. The trouble is it's not there yet. Trivial games and demos have major performance issues, so I'm not touching it.

LambdaHack - this is a rogue like game engine written in Haskell, and boy is it intimidating to a newcomer. This is a massive project with no documentation and sparse comments. I also heard it doesn't use functional reactive programming which is supposed to be the new hotness in Haskell development. I'll explain to you what that means once I figure it out.

What I have found that is really helpful is this tutorial for a Haskell roguelike:
http://jamiltron.com/2012/07/Code_Us_Some_Roguelike_in_Haskell.html
Unfortunately, the author stopped after two posts, but he at least got me past the point where I was completely lost.

It feels sketchy to store my tiles and the objects they contain in dictionaries with coordinates as keys, but that seems better than me trying to write a fast multidimensional array consisting of immutable singly linked lists.
« Last Edit: August 29, 2015, 08:41:00 PM by Yxven » Logged
rj
Level 10
*****


bad, yells


View Profile WWW
« Reply #1 on: September 30, 2014, 01:35:29 PM »

oh yeah well i'm gonna make a rogue like like like like like
Logged

Yxven
Level 0
***



View Profile
« Reply #2 on: September 30, 2014, 09:40:11 PM »

I don't think I'm allowed to express how much fun it is to set up a new unfamiliar programming environment, but I got my cabal sandbox working today. Apparently, you need to set up the project specific configuration file in order for it to do anything. Please tell me if that is a "Don't talk about fight club" type secret. I can't think of why else that fact wouldn't be included in the first 2 "learn how to use cabal sandbox" tutorials I googled.

Also, my SDL2 installation is 4 hours closer to being usable. Pro-tip: it's easier to install SDL2 if you aren't reading the instructions to install SDL1.

I look forward to banging my head on the keyboard some more tomorrow.
Logged
Yxven
Level 0
***



View Profile
« Reply #3 on: October 01, 2014, 09:45:08 PM »

Today was quite successful. I got sdl2 and the Haskell bindings working on Windows. I even explained how to do it, so that other programmers can spend more time programming and less time making up new cuss words.

http://www.reddit.com/r/haskellgamedev/comments/2i1784/how_to_install_sdl2_and_the_haskell_sdl2_bindings/
Logged
Yxven
Level 0
***



View Profile
« Reply #4 on: October 02, 2014, 09:18:28 PM »

I got sdl2 working yesterday, but I decided against trying to write something with it today. I don't feel like I know how. Learning Haskell is kind of like sprouting a third leg. You're pretty sure it's going to make you faster eventually, but in the meantime, you need to relearn how to walk.

I'm not completely incompetent with Haskell. I can solve Euler problems with it, but I can't write a simple console game. That's going to take research. I neither know how to structure a program or how to use the monads needed. The last time I tried to write a console game I couldn't even figure out which monad to use (hint it's not the state monad). In any case, that was six months ago, and I'm bracing for round two.

That said, today was a day for remedial Haskell. I worked through the first two homework assignments from here http://www.seas.upenn.edu/~cis194/spring13/lectures.html . My credit card validator came together pretty easily. I wrote almost the entire module in partial application style. I didn't initially like partial application style, but this assignment made me feel like Oprah.

"And you don't have any arguments! And you don't have any arguments! And you don't have any arguments…"

Partial application means I can write this:
Code:
digitToInteger :: Char -> Integer
digitToInteger = toInteger . digitToInt

Instead of this:
Code:
digitToInteger :: Char -> Integer
digitToInteger charNumber = toInteger . digitToInt charNumber
(This function converts a Char to a long int)

Logged
Yxven
Level 0
***



View Profile
« Reply #5 on: October 03, 2014, 09:30:52 PM »

I was tempted to skip to the harder content in that functional programming course because that's what I really don't understand, but the completionist in me forced me to do the next assignment. I figured if it really is too easy it wouldn't take very long.

I managed to spend most of the day on 22 lines of code. Here's the assignment:
http://www.seas.upenn.edu/~cis194/spring13/hw/03-rec-poly.pdf

Here is my code:

Code:
import Data.List

everyNth::  Int -> [a] ->  [a]
everyNth n xs = map snd $ filter (\ (i, _)  ->  mod i n == 0) $ zip [1..length xs] xs

skips :: [a] -> [[a]]
skips xs = map (flip everyNth xs) [1 .. (length xs)]

localMaxima :: [Integer] ->  [Integer]
localMaxima (a:b:c:xs) = if a < b && b > c
    then b:localMaxima numbers
    else localMaxima numbers
    where
    numbers = b:c:xs
localMaxima _= []

histogram :: [Integer] -> String
histogram xs = concat . transpose . map pad groups
    where
    groups = group . sort xs
    height = maximum . map length groups
    pad ys = replicate height ' ' ++ replicate (length ys) '*'

My histogram function won't compile, and I'm too sleep deprived to see why.

(I didn't try too hard on the code golf, but I did try not to use recursion)
Logged
Yxven
Level 0
***



View Profile
« Reply #6 on: October 06, 2014, 06:41:04 PM »

After spinning my wheels for a week trying to figure out how to get pixels on the screen in Haskell, I think I'm finally at a point where I can start coding the game. I decided against using SDL2 bindings. I feel like they're probably the professional choice, but trying to figure out how to program C with Haskell with very incomplete Haskell SDL2 documentation makes me want to stab my eyeballs. I'm sure someone who has experience with SDL or Haskell could do it, but there's far too much information to process at one time for a newcomer.

I retreated to using Gloss. I originally dismissed Gloss because it is a self-described vector graphics library, the documentation wasn't working, and the examples won't compile on Windows. However, I learned it can also render images from other formats, the older documentation is hidden but still available, and the code for the examples will run if you don't rely on the package and compile it yourself (at least this is true for the simpler examples that I tried).
Logged
koiwai
Level 1
*



View Profile
« Reply #7 on: October 06, 2014, 11:30:56 PM »

Agreed, having a good and simple example that actually works helps a lot.

I really like the devlog, and the programs, where you avoid recursion Wink Cool stuff. Btw, I looked at LambdaHack in the past, and had an impression that it's a rather orthodox roguelike engine. So, probably it's a good thing that you are doing everything yourself.
Logged

Yxven
Level 0
***



View Profile
« Reply #8 on: October 08, 2014, 12:09:39 PM »

My game comes from an awkward, questionable beginning. Look at the example I have to learn from:


https://github.com/mchakravarty/gloss-game/tree/master/examples/bounce

It matters not. This flying green penis will provide a strong foundation for my game.

I spent yesterday trying to figure out how to load tiles from my spritesheets into my game. I originally thought this was going to be easy. Gloss-game lets you load pngs with a simple function:

Code:
png :: FilePath -> Picture
(it's basically Picture loadPng(String filePath){..})

The trouble is I don't have all of my tiles saved in 500 different pngs, and I don't know how to crop a Picture. I only know how to crop Juicy pixel Images. "Not to worry," I thought. "I'll just look at the source and see how they converted it from the juicy pixel image to begin with." "Dear God..., it's unsafePerformIO."

I may not know much about Haskell, but I know I'm not supposed to use that function. I'm supposed to: "Have fun - use a monad."

In any case, my desire for my code base to remain pure took all day. Haskell is quite interesting in that it can make me feel like a complete programming novice. I can sit down and write quality code in most object-oriented languages without having to study the language. My code may not be idiomatic, but it will be well structured and develop reasonably quickly. With Haskell, I spend most of my time debugging error messages I've never seen in my life. I'm not saying the error messages are bad. They usually help me see the problem. However, I don't always know how to solve that problem. Monads in particular are very strange coming from a mutable background.  That said, after struggling with IO all yesterday, it clicked for me this morning, so I got my sprites loaded.
Logged
Yxven
Level 0
***



View Profile
« Reply #9 on: October 09, 2014, 09:09:46 PM »

Well, now we get to the part where I have to figure out how to actually code this in Haskell. I want very flexible enemies, so my first thought was to create an entity component system.

I spent a lot of time researching how to write an entity component system in Haskell. There are a lot of opinions on how to do it, and surprisingly, almost no one said anything like "why are you trying to bring your OO design patterns into a functional programming language?" I was expecting to have to learn a new way of designing programs, but instead, I am learning that the difference between traditional "object-oriented" programming languages and "functional" programming languages is that functional programming languages are much better at keeping things DRY.

It's Haskell in particular that is quite different from anything else (including other semi-popular functional programming languages I've heard) because Haskell cares about keeping functions with side effects and functions without side effects (pure functions) separate. This is what is really responsible for having to learn how to restructure code in Haskell.

That said, I asked the exceptionally smart people in #haskell-game (freenet) about how they would write an entity component system, and they recommended using functional reactive programming to create it. I looked at Netwire5. The examples made a lot of sense, but I still don't have a clue how to implement a game with it. I feel like part of my problem is that they are built upon stuff I don't understand. I tried suffering through more of Learn You a Haskell for Great Good, but then I rage quit. That book is far too verbose.

I hope to come back to FRP when I have a stronger grasp on the basics, but until then, I'm going with the solution I understand.

This is my first attempt at an entity component system in Haskell:

Code:
import qualified Data.HashSet as Set
import Control.Monad.State.Lazy

data Component = Component
    String                         --Name
    (Maybe Entity -> (IO Entity))  --update component
type Components = Set.HashSet Component

data Entity = Entity Int Components
type EntityManagement = State Int

generateEntityId :: EntityManagement Int
generateEntityId = do
    n <- get
    put (n+1)
    return n

createEntity :: EntityManagement Entity
createEntity = do
    serial <- generateEntityId
    return $ Entity serial Set.empty

I plan on using this for everything that would appear on the map. In theory, I will never have to change this code. In practice, I haven't written a component yet, so I'll likely have to change this tomorrow.
Logged
rj
Level 10
*****


bad, yells


View Profile WWW
« Reply #10 on: October 09, 2014, 09:13:21 PM »

It matters not. This flying green penis will provide a strong foundation for my game.

i'm literally sitting here in legitimate awe of these two sentences
Logged

Boreal
Level 6
*


Reinventing the wheel


View Profile
« Reply #11 on: October 09, 2014, 09:24:52 PM »

I've had an idea for a while about how a (technically property-centric) ECS can be implemented functionally.  The key is to think about data-oriented design and how functional programming works so well with it.

Think of how a vertex or fragment shader works: you are basically expressing a change of state as a pure function because shaders cannot have side-effects.  If you apply this to the game loop, you can think of the game loop as a single iteration of the transformation from the previous world state to the next.

So basically every piece of the next world state is expressed as a function of (some part of) the previous world state.  An object's next position is a function of its current position and velocity.  The next image of a character's run animation is a function of its speed and time.  The next decision an actor will make is a function of its goals and persistence to continue with the current goal.

While you are completely eliminating implicit temporal coupling and mutable state, one drawback to this design is that sequences of events will lag behind.  For example, if a bot decides that it should jump, it won't actually jump until the next frame, and then the next frame after that it will update its animation.  You can solve this by bucketing updates (lazy evaluation should help with implementation) so that you can make barriers between input/AI, logic, and rendering (or more steps if you want) to reap the benefits of a more traditional game loop.
Logged

"In software, the only numbers of significance are 0, 1, and N." - Josh Barczak

magma - Reconstructed Mantle API
Yxven
Level 0
***



View Profile
« Reply #12 on: October 09, 2014, 10:25:53 PM »

I've had an idea for a while about how a (technically property-centric) ECS can be implemented functionally.  The key is to think about data-oriented design and how functional programming works so well with it.

Think of how a vertex or fragment shader works: you are basically expressing a change of state as a pure function because shaders cannot have side-effects.  If you apply this to the game loop, you can think of the game loop as a single iteration of the transformation from the previous world state to the next.

So basically every piece of the next world state is expressed as a function of (some part of) the previous world state.  An object's next position is a function of its current position and velocity.  The next image of a character's run animation is a function of its speed and time.  The next decision an actor will make is a function of its goals and persistence to continue with the current goal.

While you are completely eliminating implicit temporal coupling and mutable state, one drawback to this design is that sequences of events will lag behind.  For example, if a bot decides that it should jump, it won't actually jump until the next frame, and then the next frame after that it will update its animation.  You can solve this by bucketing updates (lazy evaluation should help with implementation) so that you can make barriers between input/AI, logic, and rendering (or more steps if you want) to reap the benefits of a more traditional game loop.

This sounds a lot to me like functional reactive programming.

http://www.haskell.org/haskellwiki/Functional_Reactive_Programming
Logged
Yxven
Level 0
***



View Profile
« Reply #13 on: October 10, 2014, 09:03:30 AM »

Actually, that sounds a lot like the game loop I'm already using that comes with gloss.

Code:
playIO
:: 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 -> IO Picture)
An action to convert the world to a picture.

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

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

-> IO ()
Logged
Boreal
Level 6
*


Reinventing the wheel


View Profile
« Reply #14 on: October 10, 2014, 09:25:40 AM »

Just curious, why do your input and logic functions end up returning the world encapsulated in an IO monad?
Logged

"In software, the only numbers of significance are 0, 1, and N." - Josh Barczak

magma - Reconstructed Mantle API
Yxven
Level 0
***



View Profile
« Reply #15 on: October 10, 2014, 02:56:36 PM »

Just curious, why do your input and logic functions end up returning the world encapsulated in an IO monad?

I'm not quite sure what you're asking, but I've a few answers to your question.

1) It wasn't entirely my decision. My functions are written that way because they match the function signature of the playIO function that I didn't write. I'm using the playIO function because it is simpler than figuring out Gloss at a deeper level than I really want to.
2) "World" is really just a poorly named "GameState" and doesn't necessarily reference anything resembling a map. The example looked like:
Code:
{playerPosition: (0, 0), platformPosition:(0, -1)} 
3) IO works kind of like an infectious plague in Haskell. If I want input from outside my program (like from the keyboard or a decent random number generator), I have to mark everything from the function that wanted the input all the way down to the main function as IO because it is no longer "pure" and no longer has the same performance characteristics.
4) I'm new to Haskell, and I don't know a better way.
Logged
koiwai
Level 1
*



View Profile
« Reply #16 on: October 10, 2014, 10:56:50 PM »

I don't know Haskell really, so I may be wrong, but I thought typeclasses must be a good way for defining a generic game entity. Basically, a typeclass is an interface, a set of function signatures. If you provide all needed functions, you can separately define a type for a monster, a type for a weapon, etc. But because all of those types would have a common interface, say a function "update :: world -> t -> t", and a function "coords :: t -> (Int,Int)", your game loop will think that they are kinda the same thing.
Logged

Yxven
Level 0
***



View Profile
« Reply #17 on: October 12, 2014, 10:34:24 AM »

I don't know Haskell really, so I may be wrong, but I thought typeclasses must be a good way for defining a generic game entity. Basically, a typeclass is an interface, a set of function signatures. If you provide all needed functions, you can separately define a type for a monster, a type for a weapon, etc. But because all of those types would have a common interface, say a function "update :: world -> t -> t", and a function "coords :: t -> (Int,Int)", your game loop will think that they are kinda the same thing.

My understanding is that type classes are like interfaces in the sense that they guarantee certain methods to exist, but they are unlike interfaces in the sense that I can't create a collection of various types that implement that interface. I can create a collection that demands objects that have implemented that typeclass, but all of my objects in the collection will be the same type.

E.g.
This works
Code:
import qualified Data.Set as Set

main = do
    let s=Set.insert 1.0 $ Set.insert 2.5 Set.empty
    let t=Set.insert  "whatever" $ Set.insert "oho" Set.empty
    return ()

This doesn't work
Code:
import qualified Data.Set as Set

main = do
    let s=Set.insert "whatever" $ Set.insert 2.5 Set.empty
    return ()

(Sets require their values to be of typeclass Ord which means that they are orderable)

So while I can make a bunch of different component types that all have the updatable typeclass, that doesn't really simplify my life. I think there are a few workarounds like having all of my components serialize to a string and store strings, but that can't be good for performance. I think there are also some template Haskell macros that can make this work, but I think doing that is considered an anti-pattern.

http://lukepalmer.wordpress.com/2010/01/24/haskell-antipattern-existential-typeclass/
« Last Edit: October 12, 2014, 10:44:38 AM by Yxven » Logged
Whiteclaws
Level 10
*****


#include <funny.h>


View Profile
« Reply #18 on: October 12, 2014, 12:43:44 PM »

It matters not. This flying green penis will provide a strong foundation for my game.

i'm literally sitting here in legitimate awe of these two sentences
And I'm laughing my ass off
« Last Edit: October 13, 2014, 03:06:57 PM by Whiteclaws » Logged
Yxven
Level 0
***



View Profile
« Reply #19 on: October 12, 2014, 06:33:02 PM »

Unsurprisingly, my entity component system was fatally flawed. It's main problem was that my components could not access anything from the rest of the universe. This is great if you are trying to simulate extremely isolated objects in space, but unfortunately, I want my player to be able to "interact" with himself and others. I will share my new entity component system when my game compiles again.

I spent the last two days divining syntax errors and trying to deduce the state monad. I think I finally got it.

It's basically syntactic sugar for:

Code:
def counter(oldState):
return (oldState, oldState + 1)

Once I figured out that I would still have to route state all over my program, I was like, "fuck monads." "I'll keep it simple, so that future readers can skip the PhD reading material."

A few hours later, "routing my state everywhere is super tedious." "Doesn't Haskell have something to help me with this?" "Wait, aren't monads good at piping data to each other?"

Facepalm
Logged
Pages: [1] 2 3
Print
Jump to:  

Theme orange-lt created by panic