Sense i6 progress

Hello everybody,

Time to pick myself up and make some progress again. I've collected your feedback and made the featurelist for i6:

- Change healing station graphics and lower healing speed
- Simple how to play image to explain controls and what the game is about
- Enemyshots should hurt other enemies moderately. Though not a very good way to get a score, combined with the other weapons it could be deadly in a threatening situation.
- Change to time-based survival instead of a certain amount of spawns (everyone who survives would previously get the same score)
- Basic highscore table
- Arrows work for movement in addition to WASD
- Mouse sensitivity option
- Mouse scrolling for weapon switch
- Music and FX volume settings

Now i'm off to work!

users avatar

Explosions and mayhem yay!

Umm it kinda wasn't on the list but i've wanted to do this for 3 years when i planned this engine and i suddenly noticed i was 99% there. So i just had to. So without further ado, and a few Z-issues, here's my first effect using my awesome visual effect chain system:

http://www.solidcore.se/explosion.avi

Isn't it cool Laughing out loud

---------------
Solid Core Entertainment
Developer of Roadclub and Sense: Survival Prelude (Developer blog)

users avatar

Damn cool! :)

I'm actually working on something similar at the moment, mostly due to the fact that I'm working with a "real" artist on my current project.

When first committing to creating such a "system" I was in a pretty bad mood, because I didn't really feel that the complexity (not to mention all the editor code, persistence, etc) was worth the pain of coding it.

I was also extremely worried that a lot of the implemented functionality would go unused, simply because artists in my experience like to think and talk about "potential", but when it comes down to actually creating something in a flexible system they tend to not do anything at all.

My current art-guy is happily proving me wrong though!

/johno
"you can't stop the change"

users avatar

Oh cool, is it for a

Oh cool, is it for a sop-project? I'd like to work with a "real" artist at some point. Is there any more information available on this project?

It is indeed very complex, its my first time creating something like this and it is in no way optimally coded. i have no tools to make the effects yet. an effect editor would be awesome to have to play around in. I've already planned ahead a bit for it in code but it feels like a huge undertaking and i'm already really struggling with putting the time where it counts, in fun gameplay.

it would be good to let the artist actually make the demands of the system before making it. in my case, that's me Smiling but in your case it would be clever to let him design things before you make all the details of the system. there should be quite a bit of basics you'd always need for effects depending on game type.

so you mean you've already made a flexible system and he's using all its potential? Does he miss any functionality?

---------------
Solid Core Entertainment
Developer of Roadclub and Sense: Survival Prelude (Developer blog)

users avatar

Effect system

No this one won't be on SoP, and no information as of yet, but I want to get it finished by around the first of July, so stay tuned.

I did actually have the artist describe to me what he wanted to see. To be fair, I had already coded a simple system that scaled sprites between 2 radii while fading the translucency from 1 to 0 over time. I had hard-coded some proxy effects using that.

The artist looked at that and responded with the things he would like to have more control over; multiple "events" per effect, so that an explosion could consist of multiple explosions that were spaced out over time. Apparently this was something learned in the trenches of game development; it simply looks cooler and more dynamic.

That in itself caused things to become quite a big more complex, with both more data classes and dynamic run-time classes. The artist also wanted to be able to interpolate between more than just 2 radii, so I went ahead and implemented key-frame envelopes for radius, opacity, as well as the frame of the sprite on the texture page.

All during this effort I built the editor for this (using IMGUI), and when I had ported my previously hard-coded effects to the new system I presented it to the artist. Initially, as I had suspected, he didn't really understand the system / editor.

This was pretty much expected, as I have experience with artists not thinking through what they had "ordered" feature-wise, but after some explanation and experimentation he started getting into it. To my great surprise and pleasure he managed to create something that I would never have thought of in a single test; an explosion that starts with a big radius and then scales inward to nothing, with a second event that is a bit late and explodes outward. It looked and felt really cool!

So I guess I have to say that it was worth it. A big part of it is having the edit-test cycle be as painless as possible, because otherwise neither the coder or the artist will be inclined to experiment and find all the cool stuff.

Along the same lines I have exposed most every constant in the code to be editable and persistent (things that I would typically use #define or static const in .cpp files. This of course added more complexity, but again was worth it for me as a coder, because even if the edit-compile-link-run cycle isn't too long, you can't beat instant feedback with an integrated editor.

I guess that this project will be more data driven than most of the stuff I do. That is of course due to the involvement of a non-coding participant (the artist), but I'm still trying to keep it as tight as possible.

One thing that I don't think I will allow is for the editors in the game to arbitrarily specify dependencies on external files, because this always a pain to track when packaging builds.

Right now I have a pattern for external assets, I use:

#define ASSET_SOMETHING "somefile.ext"

For deployments I have built a tool that takes a simple text file specifying the MSVC projects to scan for assets, as well as the executable and anything else that can't be specified this way. When run it reads the .vcprojs in question (they are XML) and parses out all the assets and packs them all together into a .zip using WinRAR in command line mode.

I can then simply unpack the .zip to the desktop and run the executable, quickly verifying if I got all the assets.

This works fairly painlessly as I tend to create games that load all the assets on startup and keep them in memory at all times. This is of course not true in all cases, but since I stick to my pattern of defining assets for most things, it helps a lot.

/johno
"you can't stop the change"

users avatar

Cool stuff there! Good to

Cool stuff there!

Good to see that you found some time for actual development there Benny! Effects looking good Smiling

My experiences with effects are much like johnos and for my own projects I tend to do "immediate mode effects" I'e I code a function for each effect parameterized with a normalized timer (0.0 - 1.0). On top of this I've found that you can do a lot of funky stuff using the HLSL shaders to spice up the effects (In Project L the entrance and exit particle effects are one simple set of particles that are rendered in multiple passes to create the more complex visuals seen in the game). This all relies on having a "coder" making effects though and having an artist on board definitely changes everything.

It will definitely be exciting to learn more about your "secret" project there johno Smiling

users avatar

johno: oh so you finally

johno:
oh so you finally have funding and work fulltime on this game?

cool to hear a little about how your cooperation works in practise.

does this pattern mean that he must ask you to include his assets in the right place in the project?

great to hear how you handle assets. I enter all stuff in an xml-file where all receive a tag that can be used in code. and the game exits with a message if something is missing Smiling

my effect system is built on behaviours, particle systems and effect chains.

behaviours describe parameter changes in particles.

a particle system manages all particles for a single texture and updates them based on added behaviours.

the effect chain has a timeline that "fires" particle systems on specified frames.

its probably overly complicated but it works. its quite simple to use to built these awesome effects at least. behaviours can be reused and combined between particle systems.

in the view i build everything in a function where i create a bunch of particlesystems and add properties and behaviours to them. and then i add the systems to the effect manager.

the views effect manager receives PlayEffect commands from IEventTarget from the controller. and the view draws whatever is playing in the effect manager.

any thoughts? Smiling not very immediate mode, i know. i'll probably rewrite it some day. right now i want to finish my initial minimal plan for sense survival.

hobbe:
adding shaders on top of this would be cool but this system is probably not built for that.

what do you mean by normalized timer? is the timeline always from 0-1 but takes different amounts of time in reality?

---------------
Solid Core Entertainment
Developer of Roadclub and Sense: Survival Prelude (Developer blog)

users avatar

Funding...

Funding... well, I have my own business, and the artist is currently unemployed, so I guess we're both footing the bill for this one.

Up to this point I have created all the proxy assets for the game, and the artist simply replaces the files with his own work after the fact.

In that sense the game isn't terribly data driven; everything is very explicitly required by the code. I guess you could say that I pretty much know what I'm shooting for, as opposed to making an open ended system and hoping for the best... Smiling

/johno
"you can't stop the change"

users avatar

so you're doing this under

so you're doing this under your programming consulting banner. it does enough to keep you alive then? must be great.

how does your artist feel about all assets already being placed? doesn't it prevent some creativity and potential for visual improvement that you may have missed?

---------------
Solid Core Entertainment
Developer of Roadclub and Sense: Survival Prelude (Developer blog)

users avatar

Work

Yeah, I've been freelance full time since April of 2008. I'm paying a lot of taxes so I guess that I'm doing something right... Smiling

This year has been pretty slow when it comes to consulting work, so I'm focusing on game development and other internal investment, trying to take myself more seriously.

I sort of understand your question about the assets being placed, because it's sort of the data-driven vs hardcoded thing. I'll try to explain further...

Things like the effects can only be edited using the in-app editor. Those effects use a single texture in the interest of batching, so no real need to add or remove any external assets. Of course the artist can change the texture.

The effects themselves are persisted in a text file using my persistence backend, but the format itself isn't really human readable / editable; it has some names that are meant to be edited in case I need to change the names of classes or class members, but mostly it's a bunch of indexes and values.

Like I mentioned earlier, the effect system in itself is something that I wouldn't have implemented at all unless the artist had requested it, and sure, that's the kind of thing that has paid off simply because it allows the artist more freedom. Sort of give and take.

I'm also working on a skybox solution right now, and that is also based on a single texture (at least per skybox / level). The meshes for the skybox layers are generated by code, and the uv mapping is implicit based on a scheme that I came up with that maximizes texture usage.

In this case I expect there to be the desire for a number of different textures, but it's simpler for us to discuss exactly how many we need and just hardcode the file references in the app using the #define ASSED_XXX pattern I mentioned earlier.

The alternative would be to allow the editor in the app to browse the file system, but that would require either "manually" remembering all the assets that the game needs for deploy, or writing some kind of app-specific plug in to the deployment tools, or scanning the persistence files for file extensions, or something...

I don't really want to go there personally, because I want to have control over the files required and not have things get out of control.

Also, the request hasn't really come up yet. I'm sure that if the artists REALLY REALLY wants to be able to mess around with arbitrary file linkage, we'll discuss it, but up to this point he's actually been pretty backlogged trying to replace all of my proxy assets.

I'm sure that lots of this is due to my style of programming, in that I use lots of static allocation and try to make decisions about how many of each thing I need. Some things, like the effect types and skyboxes, are and will remain open ended in number, but for lots of things I find it makes for simpler code to just have a fixed array of things, and just bump the constant and recompile if I need more.

Also I feel that making "systems" that are too open-ended and "free" is a symptom of not knowing exactly what the goal of the game is, and that's something that I'm very conscious of. Projects often get off track in this way.

/johno
"you can't stop the change"

users avatar

hehe good for you What i

hehe good for you Smiling

What i meant was that if all assets are already in place, don't you allow bigger artistic changes that would say remove an asset or add a couple of extra. How much flexibility towards the artist do you have in terms of game changing changes as opposed to just replacing what's already there?

i'd like to hear more about the persistence system. in roadclub we just made save/load functions in each class that saved its members to a stream sent as parameter. it worked pretty well i think.

i agree browsing the file system wouldn't be a good idea unless the game copied the asset to the project so you could keep track of them. but if the artist is happy with your current workflow there's no need to change it.

you make a good point and you've actually pushed me in that direction. i find myself coding more statically nowadays.

i can also see how flexible systems can push projects off track. sometimes i just make stuff to get them done though, i don't think i have the experience to plan that far ahead yet. games are especially tricky because it's not just about if they "work" or are "easy to use", they also have to be FUN and that means there needs to be experimentation and reiteration to get it right. that means sometimes you just have to make something so people can try it even if it isn't made the best way i think. if its good you probably want to change it anyway.

hmm this wasn't meant to be an excuse to code overly free systems but i guess that sometimes can be the case when working like i do above. because you want to use what you know just to see it on-screen rather than figuring out how to do it the best way at this point. usually you don't at that point anyway because there's an ever better way lurking around the corner.

---------------
Solid Core Entertainment
Developer of Roadclub and Sense: Survival Prelude (Developer blog)

users avatar

Trends

About art assets...

Well, lets just say that the code is in no way set in stone, what I've been describing is basically just the trend of how things get integrated. First I build a feature using proxy art, then I talk the artist about anything that needs to be changed in the way the specific feature works and / or interfaces with the specific assets required, and then the artists is free to play around with it.

Concerning persistence

I've been playing around with variations on the same basic idea for about 5 years, and it all boils down to the my need for what relational theory would call "data schema evolution".

Since I use MVC and IMGUI to create a lot of editors and tweakers inside the app itself, the app is the "source" of the data in most every case that doesn't make more sense to do in something like a 3d package, Photoshop or an audio editor. I think those are the only things that I "import"; meshes, textures and wav/ogg files.

Since the data generated and needed by the game tends to evolve over time, I need a way to be able to "migrate" parts of the data in the persistent files without losing everything whenever I change some class (delete or add a member).

In some cases I have to change the name of a class or a class member, and that's one of the reasons that the files are text so I can simply make the same changes to class names in the persistence file as in the code.

If you use a stream solution, you end up being severely bound to order of persistence issues (as a stream is basically one dimensional). What one needs is a way to explicitly name each bit of data that you save or load in a globally unique fashion.

First this means the instances of persistent classes need unique ids. I use the relational analogy of classes being tables and objects being rows in said tables. Class names are pretty much unique, at least within namespaces, so I use string versions of classnames as part of the identifier.

Each object, at run-time, is unique because it resides at a unique memory address (the value of the this pointer). This, unlike the classname, isn't the same for each execution, so the persistence baseclass constructor automaticlal keeps a class wide list of instances and enumerates all objects with a unique unsigned int key. This gives me a persistent object id, also a part of the identifier.

Within the scope of the class, the member names are already unique (because C++ requires that), so string versions of these are used.

With all of this in place, the persistence backend (I call in the "persistence context") is basically an object from which you can pull data members and to which you can push data members. The unique format for any piece of data in the program is thus:

classname/object_key/member_name

with the types of same being:

string/unsigned int/string

So the persistence backend datastore is simply key/value pairs, something like:

classname/object_key/member_name = value

The system uses abstract datastores for flexibility, but for now the only implementation I have stores to simple text files.

Again, the key is figuring out a way to uniquely identify every piece of memory you want to persist, and then have a datastore the supports pulling and pushing the data based on that identifier.

That's where the migration capabilities come from; since I'm not accessing data as a stream, the system is robust against changes in order of persistence. Every piece of data that I need to load is explicitly pulled by name. How the specific backend implements that is hidden behind the interface.

Lots of variations of this have been tried, but in the end the way I use it now simply boils down to those 2 things:

1) Unique data ids (that work across program executions)
2) Non-streamed / traversable data storage (i.e. "let me access the value of this globally unique key...")

More on the analogy to relational theory

A benefit of the persistence baseclass is that all objects of a certain type reside in a class-wide instance list; this means I don't have to use containers for classes that have an unbounded number of instances. In fact I almost never use template-based lists or arrays (like STL offers) at all anymore.

There's nothing magic about that, objects can still be statically allocated, and I use that a lot too, but I like the relational analogy that all instances (rows) of a given class (table) are in the same place.

There's a lot of code like:

new Thing;

Thing* t(Thing::first());
while(t)
{
t->operation();
t = t->next();
}

Thing::deleteAll();

All of those things are supplied by the persistence baseclass.

The basic way to load and save an unbounded (instance wise) class would be:

void loadThings()
{
persist::TextContext c;

c.read("things.txt");

Thing::pullAll(c);
}

//do program

void saveThings()
{
persist::TextContext c;

Thing::pushAll(c);

c.write("things.txt");
}

For any given persistent class, like Thing in this case, a basic member pull is:

myMember = pullInt(aContext, "myMember", some_default_value_in_case_there_is_no_such_data_in_the_context);

and a push is:

pushInt(aContext, "myMember", myMember);

These methods exist for all major types (just conversion to and from strings), and also do the bulk of the "classname/object_id" prefixing when accessing the context.

/johno
"you can't stop the change"

users avatar

wow thanks thats a lot of

wow thanks thats a lot of info. it sure seems simpler and more flexible than filestreams.

how does pullAll know what to take from the file? or do you use separate files for each thing?

i do use a lot of vector still but i have no persistent data at all yet since you can't save games, highscores or options yet. so it would be perfect to try this. i'm actually writing the options menu now.

this seems fairly modular, you wouldn't happen to even be willing to share the code for the persistence class?

---------------
Solid Core Entertainment
Developer of Roadclub and Sense: Survival Prelude (Developer blog)

users avatar

Details

pullAll()

The persist::IContext interface defines a method where you can get all the objects ids for a given classname from the storage, which is returned in a list.

The code then runs through this list and calls something like this (it's all templates):

for each id
{
new Class(aContext, id);
}

This requires in turn that the class you are calling pullAll() on have a constructor that takes a context and a key / id (since this is done with templates, this is only a requirement if you actually call pullAll()).

The key / id is in turn passed to the baseclass constructor, which will use the supplied key instead of autogenerating one (this is all checked for collisions of course).

In the remainder of the specific class constructor you can simply pull values from the context instead of doing the regular good practice default value assignments.

In this way you can restore all the dynamically allocated instances that you had last run-time.

For things that are statically allocated / arrays you wouldn't use pullAll(), you would simply run through your array and call a custom pull(context...) method or whatever you want.

pullAll() is supplied by the persistence baseclass as a static method, and hereby I enforce the "pattern" of forcing the subclass to have a specific constructor for use in pullAll().

Other than that there aren't any enforced methods of using the code. You ultimately always have to pass a context to the pullXXX() methods (pullInt(), pullFloat(), etc), so a decision has to be made where the data is coming from.

One way to go, for statically allocated stuff, is to have a global context that all classes simply access globally in their constructors (for pull) and destructors (for push). This makes the persistence feel sort of like a language extension; classes are automatically pulled and pushed on creation and destruction.

The only problems with this is that it feels kind of magic, and you lose the fine grained control that you might want. Also you need to be aware of quirky construction order stuff for file scope static variables, as well as the fact that changing declaration orders for arrays and such will implicitly change the instance ids (that are automatically generated) behind your back. But sometimes this works really well, as long as you're careful.

editable "constants"

I recently implemented a replacement for my typical file scope #defines and static const XXX stuff (hard coded constants) using the persistence system. In this case I didn't inherit from the persistence baseclass but just used a persistence context along with the __FILE__ macro and some string manipulation to get unique ids.

Then I built an editor where I could traverse all the "constants" and edit them. I extracted "category" from the __FILE__ macro and built property sheets for each one so it's to find what you're looking for.

So instead of:

static const float MAX_THING(56.f);

I have:

static constant::Float MAX_THING(__FILE__, "MAX_THING", 56.f);

As I mentioned, the __FILE__ macro output is manipulated a bit to get the category. Apparently the output of that macro is different in debug and release (probably some compiler setting that I couldn't find), so there is some stripping of paths and forcing to lowercase, etc.

Again, this another example of how to figure out global ids for data, and from there you can really do it however you want.

Concerning sharing code...

I don't think I'm prepared to shared code at this point, but I will gladly give you pointers if you want to do something similar. Everyone of course has their own specific needs.

I also found that using this stuff for really big arrays of persistent objects presents some unique challenges in the implementation of the datastore / context; pulls and pushes can take a long time to look up.

As a result I have come up with a pretty funky datastructure to make it even remotely fast enough, and I'm not really ready to share that code at this time.

/johno
"you can't stop the change"

users avatar

it does indeed seem to work

it does indeed seem to work best for simpler values rather than huge amounts of objects and arrays. if you need to hack it, maybe the solution is wrong?

hmm ok i'm starting to think this is overkill for sense. come to think of it, i think we made something similar with a class that saved stl-maps mapping a "tag" with a value for strings, floats and ints. Maybe i'll reuse that for now.

now that i now this exists i'll notice when my own systems suck easier.

---------------
Solid Core Entertainment
Developer of Roadclub and Sense: Survival Prelude (Developer blog)

users avatar

Yep found it

Here it is. simple enough Smiling


class CGameData
{
public: // Functions
CGameData();
~CGameData();
void Clear();
// Setters
void SetInt(string dataTag, int value);
void SetFloat(string dataTag, float value);
void SetString(string dataTag, string value);
void ChangeInt(string dataTag, int modifier);
void ChangeFloat(string dataTag, float modifier);
int GetInt(string dataTag);
float GetFloat(string dataTag);
string GetString(string dataTag);
bool Save(string fileName);
bool Load(string fileName);
bool Save(ofstream& fout);
bool Load(ifstream& fin);
private: // Functions
private: // Variables
map _IntData;
map _FloatData;
map _StringData;
};

---------------
Solid Core Entertainment
Developer of Roadclub and Sense: Survival Prelude (Developer blog)

users avatar

Wrong?

I wouldn't say that the solution is "wrong" per se, but sure, you need to choose when to apply it.

The cost of being able to traverse the datastore freely is quite large (in terms of code complexity), so that's a big tradeoff. I'm currently using a custom binary tree along with a bunch of "last accessed instance" caches in the datastore...

/johno
"you can't stop the change"

users avatar

yeah choosing the right tool

yeah choosing the right tool for the job is the best way to head in the right direction.

i was gonna ask about caches Smiling

is the main point of your system to be synced with the disk in case of power loss or something? i mean otherwise my simple class above would do the same job if i just saved it occasionally and have each class add a prefix to the tags no? could even keep track of changes and save incrementally.

---------------
Solid Core Entertainment
Developer of Roadclub and Sense: Survival Prelude (Developer blog)

users avatar

Intent of my persistence system

No, my system has nothing to do with power loss or disk sync, it's just the way I prefer to store arbitrary data to disk, especially when that data is authored in-app.

Like I mentioned, I wanted a system that was robust to "file format changes"; a binary stream couldn't give me that. Also, I needed something that matched the code well, as everything is very "code centric".

These days I will typically have a number of different "contexts" on disk; I find that about one per persistent class is pretty good granularity. I will pull the data on app startup and push it back out again on shutdown. In certain apps pushing / saving is only triggered by the user.

I will typically have the context files (text files) under source control with the rest of my data.

You are correct that your simple class would do the same job, absolutely. The challenge I suppose would be making sure all your dataTags were globally unique, but like in my system you can do this outside the scope of the storage class itself.

/johno
"you can't stop the change"

users avatar

Well i used my old classes

Well i used my old classes and it seems to work for now.

Also, yay almost done with i6.

---------------
Solid Core Entertainment
Developer of Roadclub and Sense: Survival Prelude (Developer blog)

users avatar

Great! Looking forward to

Great!
Looking forward to it! Smiling

users avatar

Not too many changes

Not too many changes unfortunately but your feedback has been heard. For i7 i hope for more drastic gameplay improvements.

---------------
Solid Core Entertainment
Developer of Roadclub and Sense: Survival Prelude (Developer blog)

users avatar

Post new comment

Please solve the math problem above and type in the result. e.g. for 1+1, type 2.
The content of this field is kept private and will not be shown publicly.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <h1> <h2> <h3> <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.
  • Textual smileys will be replaced with graphical ones.
  • Images can be added to this post.

More information about formatting options