• Advertisement
Sign in to follow this  
  • entries
    16
  • comments
    12
  • views
    27710

About this blog

Development of the Firework Factory game

Entries in this blog

Hi all,

It's been quite some time since i last posted a journal entry. This hasn't been for lack of development of the game, more lack of free time where i'm not being a Dad, working my day job, or finding a few minutes to add to the game. Unfortunately for everyone who reads my journals, writing about my progress kind of came last, and for that i'm sorry.

There have been many advancements to the game in the past three months, these are:

Porting of most of the gameplay code to C++

Certain parts of the game were quite slow. I had narrowed this down through a small profiling session to loops within Blueprints. It seems that while Blueprint is good for some things, for loops especially it has some issues, as well as casts, which cannot be resolved at compile-time like many C++ casts can, and will quickly bog down performance.
To resolve this i rewrote large amounts of the code in C++. During this time, I encountered some annoying bugs, such as this one, which I reported to the Unreal Engine developers, and they are fixing as we speak:

To rewrite my code in C++ i had to basically create a C++ base class for each blueprint and move variables first, then code a function at a time, manually rewriting it. In the process of doing this I learned a lot about Unreal Engine that i didn't know before, and this was a great learning exercise for me as well as helping improve the game's performance.

Adding new crate types

I have finally had time to add a new crate type to the game. The new crate type contains radioactive waste, and i have dubbed it the "goop crate". This crate appears later on in the game, after a major plot twist. The crate explodes with radioactive blobs of glowing green muck upon impact, which linger around and cause damage to any crates that pass across them:

I created this particle effect using CPU particles with collisions, which when they collide with a horizontal surface (by checking the surface normal) they leave a decal on the surface, which is embedded within an Actor with a timer set to fade it out by adjusting values in its material instance.

I am quite happy with the effect, and hope to find some time to add some more types of crate very soon!

Adjusting the apperance of the machines

I have decided that as i cannot get the previous artist for the game on board, and the look and feel of the game has changed anyway, i would like to go with a darker, grittier and less comedic, less cartoony feel for the game. The first part of this is to replace the comedy machines that produce the crates with something slightly more realistic. To aid this i have placed grilles on the sides of the machines and created a more realistic lamp which sits atop the machine. When the machine is due to produce a crate, a siren sounds and the lamp flashes. The flashing is realistic, working with a spotlight pointed at a mirrored surface, and a rotating lamp body which has a great effect. The model for the lamp cost me a cool $15 on turbosquid (let's be honest here, i am a programmer and i cant model something like this to save my life). Below is an example of the new machines in the game, and a development test in the editor:


As always comments are more than welcome! (it's encouraging to know that people read and sometimes enjoy what i write here). Let me know your thoughts, minor flames, or whatever comes to mind.

Bye for now and stay tuned for future developments!

Hi all,

Another day, another feature! The last couple of days have kept me busy creating a "physics powered" grab arm for picking up the crates. When the player gets the crates to the exit, the claw comes to get it. This is as creepy looking as it sounds. I decided to build the claw from primitive static mesh types, and bind the meshes together with physics constraints so that they acted in a hinged way, using forces and impulses to manipulate the arms.

The reasoning behind this is so that when an object is within the claws, the physics library (in UE4's case, PhysX) can automatically clamp the four arms onto the box, or whatever else is there, and lift it. Once the item is lifted it can let go at any point and let it drop (which it will do if you give it the wrong crate). The other approach to this would involve a skeletal mesh, rigging, and inverse kinematics. That set of options is a non-starter for me because I am extremely lousy at Blender, not even able to create something with simple shapes, never mind rig or animate a model, and secondly, in my opinion, inverse kinematics in unreal engine 4 is a bit awkward to implement and last i tried it, a bit unstable causing random crashes.

It took me a couple of hours to create a model i was happy with, using static meshes and some material work, and then another hour or so to fine tune the physics of the hinges on the arms, giving me a rather disturbing looking grab claw:


med_gallery_171313_904_239994.png


med_gallery_171313_904_273803.png

The claw can be controlled by two simple physics radial forces, one to draw the arms together into the centre, restricted by a maximum angle, and one to push the arms wide apart ready to pick something up (this is required so that they don't clip through the thing theyre trying to grab, and cause them to go completely nuts).

These actions are shown in the video below:



Finally, the proof is in the seeing, here is the claw in action, with some really cool new explosion effects!



Comments and feedback as always are more than welcome!

Hi everyone!

It has been a long time (nearly a year) since i last worked on this game. My usual development routine involves having multiple games in progress at any one time. Once i start feeling burn-out coming on, or boredom setting in with one project i flip to the other, until one or the other is complete, always having more than one ready to be developed.

This is the way it has been for me for over a decade, and it works very well for me.

To this end when restarting working on this project i took a long hard look at what I could and couldn't quickly improve in the codebase. The graphics of the game were severely lacking, with no real lighting, no shadow mapping, no physically-based rendering, none of the textures had normal maps, etc.

To add these features myself and get them looking good would take quite some time. Once they were added, i would need to implement a toolset to manipulate them correctly, and then test it all. This would also take a long time.

My other project, which some of you may have been following, was written in UE4, and this gave me the features mentioned above out of the box. I decided therefore to take my existing C++ code, and adapt it to Unreal Engine 4, using UE4 to do the rendering and sound (this also gets rid of the licensing requirements of using fmod for sound and music, as these features plus 3D audio are built into UE4).

It took surprisingly little time to get something looking pretty good out of UE4 using my existing assets, scaled up using GIMP's Sinc (Lanczos 3) scaling which prevents blockiness you usually get when scaling up an image. In the end, i plan to re-create these assets myself using edge detection to turn them into line art, then scaling the line art to suit. Currently, my old 64x64 textures have been scaled successfully to 512x512.

By being very creative, and spending the better part of 6 hours tweaking light levels, placing spot and point lights, emissive materials etc, i came up with something that pleases my eyes and looks very impressive, especially compared to the old game:

[table]
[tr][td][sharedmedia=gallery:images:7291][/td][td][sharedmedia=gallery:images:6404][/td][/tr]
[tr][td]New Graphics[/td][td]Old Graphics[/td][/tr]
[/table]

The graphics in the new version are a bit of a hack. To keep performance good when lots of particle effects will be going off, i have intended to keep FPS high at this point, and consider carefully everything i do. Therefore, the lighting must be carefully planned out. A lot of the lighting must be dynamic (movable lights) rather than static, baked lighting, as most of the items inside the room can move, including the 'play table' itself. To this end i decided that even though the game's room looks like it is indoors on a sunny day, there is actually no real sky light and no real outside environment.

Outside the room is a quad, which has an image of an outdoor scene textured onto it. The material used to texture this quad has a high emissive value (texel colour * 8.0) which makes it shine with brightness like a bright sunny day when viewed from inside a dark room. This in itself is not enough to cause light shafts to stream in through the open windows however and shadows to be cast on inside items, as the emissive materials in UE4 are a bit of a hack, which to cut a long story short, technically don't really emit light (unless you're using light propogation volumes, which are a performance hit, and still experimental even in 4.10) - it is just a post-processing effect or two.

So, to emit light, i have added a single spotlight slightly above, in front of, and to the left of the textured quad, still outside the window. The intensity of this light has been ramped up to 100,000 units, which causes a really bright light to be shone in through the window with much the same intensity as the sun on a bright summer day. For all intents and purposes, the effect is the same as being inside a room with a bright day outside. To complete the effect, as all factories and offices seem to have them, a point light has been placed just below the ceiling of the room, casting a less powerful set of shadows almost straight down. This is why when you look at the image the machines etc seem to have two shadows, one darker than the other.

The complete layout of my room (as i can't just screenshot the editor, there is no "light outside" to be cast on it to show you) is sketched below:

[sharedmedia=gallery:images:7292]

I am still undecided wether or not the high bloom effect actually contributes to the scene or detracts from it, comments on this as always or anything else are welcome :)

Another week, another feature!

This week heralds the addition of a loading screen. Partly due to a thread here on gamedev.net i decided that perhaps now was the time to add one, as shown below.



This actually turned out to be quite a challenge, due to the way i had designed my game.

At the point where the assets are loaded, the game loop is not yet running, and nearly none of the subsystems are initialised. This makes it harder than it otherwise would be to display and represent a loading screen, and leaves me grappling with a slightly alien environment without all my event driven conveniences.

I decided to approach this as follows:

Firstly, I had to adjust my sprite cache, to allow pre-caching specific named items at startup without enumerating the entire directory (which contains hundreds of files) and support for streaming objects on request:
/** * The SpriteCache class is a container object which loads and stores the Sprite2D * objects, which can be referenced by a string key. SpriteCache is accessed via the * read only [] operator, e.g. sprites["name"]. */class SpriteCache{private: FireworkFactory* creator; std::unordered_map, std::hash> sprites; Sprite2D* Get(ResId id);public: SpriteCache(); SpriteCache(const SpriteCache ©); SpriteCache(FireworkFactory* creator, const std::wstring &directory); SpriteCache(FireworkFactory* creator, const std::wstring &directory, std::vector preload); void CacheDirectory(const std::wstring &directory); void Stream(const std::wstring &directory, const std::wstring &file); ~SpriteCache(); Sprite2D* operator[] (ResId id); void ReleaseAll();};

This can then be called when starting up the game to load a select small number of 2D sprites for the loading screen only. The loading screen is made up of several pieces, a bottom part of the bomb, the top part of the bomb (there are various versions of this showing different amounts of fuse remaining), the text saying "Loading..." and the spark, which is alpha blended into the scene. When we initialise the sprite cache, we load only the first "top portion" of the bomb with a complete fuse, and as further sprites are needed they are streamed on request:
sprites = SpriteCache( this, L"Sprites", { L"bomb-bottom.png", L"bomb-spark.png", L"bomb-top-1.png", L"bomb-top-overlay.png", L"loading.png" } );

I then created a class called LoadingScreen (more on this later), and i wrapped creation and destruction of the LoadingScreen object around my resource loading calls:
loader = new LoadingScreen(this, Util::ReadDirectory(L"Textures/*.dds", true).size() // Number of textures to load + Util::ReadDirectory(L"Sprites/*.png", true).size() // Number of sprites to load + Util::ReadDirectory(L"sfx/*.wav", true).size() // Number of sound effects to load ); /* Load assets here... */ delete loader; loader = nullptr;

The second parameter to LoadingScreen is simply a figure used to calculate how many steps should build the progress meter. For this, we do a total count of all sound effects, sprites, and textures in the game by enumerating the directories, and using only the size of the returned vector in-place.

The LoadingScreen class looks like this:
/* Represets the loading screen of the game. * Note that when this is instantiated, the game loop is not yet active. * To draw and update the loading screen, you must call the Draw() or * Increment() method which renders the current frame to the render target. * The loading screen requires only DirectX and Direct2D to be initialised. * Deleting the object triggers the explosion effect. This is will block until * complete. */class LoadingScreen{private: /* Creator object */ FireworkFactory* creator; /* Sprites (pre-loaded by SpriteCache) */ Sprite2D* bomb_bottom; Sprite2D* bomb_spark; Sprite2D* top_overlay; Sprite2D* loadingtext; /* Streamed sprites (loaded on request by SpriteCache) */ std::vector bomb_top; /* Sprite positions */ D2D1_RECT_F bomb_position; D2D1_RECT_F text_position; D2D1_POINT_2F spark_position; /* Number of completed steps */ int done; /* Total number of steps */ int steps; /* Last "top of bomb" sprite. When this changes, * we request a new sprite be streamed from disk. */ int last_top; /* Waypoint coordinates for positions of the spark along the fuse. * There are actually 235 of these, each contains an X and Y * coordinate making 470 values. */ static const double waypoints[470];public: /* Create a loading screen which expects 'steps' increments */ LoadingScreen(FireworkFactory* creator, int steps); /* dtor automatically plays the explosion animation */ ~LoadingScreen(); /* Draw current frame of loading screen at done/steps*100 percent */ void Draw(); /* Add completed steps to the animation */ void Increment(int step);};

As you can see, there is a Draw() method, and an Increment() method. The Draw() method is called directly by the constructor once everything is set up, and by the Increment() method. Different subsystems call the Increment() method as they load assets, increasing the number of steps completed, and redrawing the screen with the new progress:
sprites[crc32] = std::make_shared(new Sprite2D(creator->GetDX()->D2D->GetRenderTarget(), *f)); if (creator->GetLoader()) creator->GetLoader()->Increment(+1);

An important feature of the loading screen is that when its destructor is called, it will play the 'explosion' animation, which blocks for 100 frames while it updates. This then leaves the game in a state ready for the backbuffer to be cleared and the game proper to be launched.

You might also be asking yourselves "so if no subsystems are properly initialised when the loading screen is being displayed, where do the sounds come from?" - The answer to this is simple. Because we cannot guarantee that the sound system is initialised, or that any sound effect assets are loaded, the simple sound effect for the fuse is embedded into the executable as a win32 custom resource type. We use the WinMM PlaySound() API call to play the embedded wav file:
PlaySound(TEXT("IDW_FUSE"), GetModuleHandle(NULL), SND_RESOURCE | SND_ASYNC);

Of course, by the time the explosion is ready to be displayed, we can be sure the sound system and all the sound effects are ready for use, so we stop the PlaySound call, and use FMOD to play the explosion sound:
/* Stop the fuse sound */ PlaySound(NULL, NULL, SND_FILENAME); creator->GetSFX()->PlayEffect(SFX_LOADING_BOMB);

That about sums up the trickery and deception that is my loading screen. As always, comments are welcome below :)
Over the past month I have busied myself adding lots of tiny bits to the game, some of which are obvious to players and others not.

I have added a new menu design, with sound effects and animations;

[sharedmedia=gallery:images:6400]

I have also added a new logo for the game on the menu screen.

[sharedmedia=gallery:images:6407]

I have also sped up and increased the pitch of the boss's speech so now he sounds more like an unintelligable voice on the other end of a cartoon telephone conversation - i think this sounds much nicer, and i have made the text in the speech bubble incrementally appear as the boss speaks. Every syllable of gibberish causes appearance of one new word until all are displayed, or the mouse is clicked to cancel it.

More importantly, I have also added a clock to the game. The original 2D version had this clock, but it never was finished, when it reached zero, it would do nothing.



As you can see the clock starts with its 'minute' hand at the 3PM position, and the 'second' hand makes one complete revolution per real-time second. Each revolution of the second hand advances the 'minute' hand to the position of "360 / time limit * elapsed seconds" degrees around the clock face. The clock face is drawn rather simply: clocksprite->Draw(position); float radians = minuteshand * (M_PI / 180.0f); rt->DrawLine(centre, D2D1::Point2F(centre.x + (cos(radians) * handlength), centre.y + (sin(radians) * handlength)), minutes_brush, 8.0f * scale.x); if (running) { radians = (secondshand - 90.0f) * (M_PI / 180.0f); rt->DrawLine(centre, D2D1::Point2F(centre.x + (cos(radians) * handlength), centre.y + (sin(radians) * handlength)), seconds_brush, 3.0f * scale.x); } clockglass->Draw(position); if (HurryUp && running && !creator->IsPaused()) title->Draw("HURRY UP!", titlepos, messageopacity);
When writing this code i did consider using matricies to translate the positions of the lines used for the hands, but i decided that in this case, good old-school sine and cosine seemed more readable to me, and they work well.

When at least 70% of the time allowed for the level is taken up, the clock starts to shake (as if under pressure) and a message appears on the screen stating to 'HURRY UP!'. If the player still hasn't finished the level when over 90% of the time is taken up, the clock shakes more violently still, and once all time is taken up, all crates active in the level explode, which can and usually leads to level failure. Level* lev = creator->GetLevel(); D2D1_RECT_F rumble = position; float used = Ticks / lev->TimeLimitSeconds; if (running && !creator->IsPaused() && lev && used > 0.7f) { float offset_x, offset_y; if (used > 0.9f) { offset_x = Util::Rand(-4.99f, 4.99f) * scale.x; offset_y = Util::Rand(-4.99f, 4.99f) * scale.y; } else { offset_x = Util::Rand(-2.99f, 2.99f) * scale.x; offset_y = Util::Rand(-2.99f, 2.99f) * scale.y; } rumble.top += offset_y; rumble.bottom += offset_y; rumble.left += offset_x; rumble.right += offset_x; clocksprite->Draw(rumble); } else clocksprite->Draw(position);
At each of the incremental changes at 70% of the allowed time limit and 90% of the allowed time limit, the pitch and speed of the game music increases, in a rather comical way. I got the inspiration from this from sonic the hedgehog, on the underwater levels when running out of oxygen. No matter how much i try, i will never forget the frustration of those levels and that tune always sticks in my head. float used = Ticks / lev->TimeLimitSeconds; if (used > 0.9f) { creator->GetSFX()->SetMusicPitch(1.8f); } else if (used > 0.7f) { creator->GetSFX()->SetMusicPitch(1.5f); HurryUp = true; }
In many levels the addition of this clock will add extra challenge to the game, and was a missing game mechanic i have been waiting until now to implement.

Next on my to-do list is power ups. I have several of these in mind each of which can be purchased with points acrued from successfully completing levels:

  • A glue gun, which makes a piece of conveyor sticky, pasting a box to it which won't move for a while
  • A grease gun, which has the opposite effect, accellerating any box to pass over it
  • A grab claw, like those seen in amusement arcades, which takes any selected box and deposits it somewhere else of your choosing
  • A spanner, which when thrown at a machine disables it for a while and stops it producing crates, changing the order of production

    There is room for more (and i'm open to suggestions in the comments!) but these were the four that i planned to have level designs to exploit.

    Comments as always are welcome! smile.png
It's been a month or so since i last wrote a journal entry here, but this has not been due to lack of work on the game.

Rather the opposite in fact, i find myself spending so much time creating the game that i have very little time left in my life to write journal entries! Oh, what a first world problem it is.

Of many things i have busied myself with over the past month, one of them has been rewriting and optimising how resources are managed within the game.

I have settled on doing this in a particular way which i thought it worth sharing with the rest of the community.

Firstly, each resource has an ID key which is called a ResId, and is 32 bits long. This is a strongly typed value:BOOST_STRONG_TYPEDEF(UINT32, ResId);
This actually creates a class containing just the native type (UINT32) with various operators for assignment, copying and comparison. It ensures that I am forced to use "ResId" as my variable type all through my code and not get lazy and start using UINT32, which means that if i choose to change the size of ResId (for example to 64 bits) at a later date, this can be done relatively painlessly in one place.

ResId types are calculated at compile time by a pre-build step. The pre-build step is a perl script (attached at the bottom of this journal entry, if you are curious!), which enumerates a set of directories and builds a header file, which is full of definitions like this:/* Compile time hashes for files within the Textures directory. * Automatically generated by Util/makehashes.pl. DO NOT MODIFY * by hand! */#pragma once#define TEX_CONVEYOR (static_cast(0x4eec1561)) /* Textures/conveyor.png */#define TEX_CRATE_BREAKABLE (static_cast(0x64d3a7f5)) /* Textures/crate-breakable.png */#define TEX_CRATE_DYNAMITE (static_cast(0x57f6ebd3)) /* Textures/crate-dynamite.png */#define TEX_CRATE_MORTAR (static_cast(0xb1ddcc8a)) /* Textures/crate-mortar.png */
As you may have noticed, each of these is a simple CRC32 value of the constant name, e.g. 0x4eeC1561 is a CRC32 of "TEX_CONVEYOR". The script which generates these header files only updates them on disk in the event they need to change, to avoid needless rebuilds of the project dependencies.

Armed with this metadata which we built at compile time, we are now able to define a texture cache class:/** The TextureCache preloads all textures into SRVs and allows access to them via operator[]. * It may throw a std::runtime_exception when constructed with a directory name and DX11 object, * if it cannot load a texture. All textures are loaded from a virtual directory within the zip * file on PhysicsFS. */class TextureCache{private: /** All resources are stored in an unordered_map which uses a nerfed hash function. * Performing std::hash on ResId always just returns the same ResId, as ResId's are hashed * already at compile time. */ std::unordered_map, std::hash> textures; /** Used internally by operator[] */ ID3D11ShaderResourceView* Get(ResId id); /** Builds a descriptive string with an error text and error number from a windows stringtable * resource, and throws it as a string within a std::runtime_error exception object. */ void ReportError(HRESULT hr, UINT caption_id);public: /** Create empty but initialised object, used when assigning inplace */ TextureCache(); /** Copy constructor, usage of CComPtr above ensures that we don't lose our SRVs * when we copy, so this is mainly just to copy the unordered_map itself. */ TextureCache(const TextureCache ©); /** Initialise texture cache by reading all textures from a given directory using WIC. * The directory is virtualised via PhysicsFS. The DX11 object contains the context and * device upon which the SRVs are to be associated. */ TextureCache(DX11* dx11, const std::wstring &directory); /** Release all CComPtrs by nulling them */ void ReleaseAll(); ~TextureCache(); /** Look up any given texture resource by ID, e.g: * ID3D11ShaderResourceView* srv = cache[TEX_CRATE_DYNAMITE]; */ ID3D11ShaderResourceView* operator[] (ResId id);};
You might have noticed something strange about the hash function i am using. I have written my own hash function for "hashing" ResIds, which effectively does nothing. This is because ResIds are hashed at compile time, so any further implicit hashing would be a time wasting exercise:namespace std{ /** This is a nerfed hashing function for resource caches where unordered_map is used. * To prevent the unordered_map doing anything with the Resource Id, we just return it * verbatim here. Otherwise, we can't gaurantee that the hasher for unsigned int won't do * anything to the resource id, wasting time. There's no use hashing a value at runtime * that's already been created as a hashed value at compile time. */ template<> struct hash { inline size_t operator()(const ResId &v) const { return v; } };};
We can then construct a texture cache simply and effectively on startup, without any needless heap allocation as follows:textures = TextureCache(dx11, L"Textures/*.png");
When called, this constructor enumerates the content of the Textures directory, finding all PNG files, and loading them with WIC. Each one it loads, it builds a CRC32 value of the constant name (which it determines from the filename) and stores it in the unordered_map keyed by its ResId:TextureCache::TextureCache(DX11* dx11, const std::wstring &directory){ HRESULT hr; const FileList tex = Util::ReadDirectory(directory, true); for (FileListIter f = tex.begin(); f != tex.end(); f++) { CComPtr LoadedTexture; size_t texturelen = 0; const char* texturedata = Util::ReadWholeFile(*f, texturelen, true); if (texturedata) { hr = DirectX::CreateWICTextureFromMemory(dx11->Device, dx11->Context, (const uint8_t*)texturedata, texturelen, nullptr, &LoadedTexture); if(FAILED(hr)) { ReportError(hr, IDS_ERROR_LOADING_TEXTURE); } else { std::string processed = Util::ProcessFileName(Util::GetFileName(*f), "TEX_"); ResId crc32 = static_cast(Util::CRC32(processed)); DebugOut() << "Loaded texture " << processed << " (" << Util::WStringToAscii(*f) << "), ID: 0x" << std::hex << crc32; Util::SetDebugName(LoadedTexture, processed); textures[crc32] = LoadedTexture; } } else { ReportError(0, IDS_CANT_READ_TEXTURE); } }}
You might also have noticed that there is no operator[] method used for assignment. This is on purpose, so that if i accidentally ever type "cache[VALUE] = someSRV;" this will be caught as a compile-time error. Such an action is not expected and should be forbidden at this point.

There are a few gotcha's with this implementation, the most important being hash collisions. It is possible, but unlikely, that I can get hash collisions by using CRC32 to hash the resource IDs. If this does become a problem, I will look to moving to CRC64 or FNV-1a instead. Time will tell if this will prove to be a problem at all.

A future enhancement would be runtime loading of new textures, and releasing of unused textures to stay within a memory budget. Right now, i have enough memory and small enough number of textures that this isn't required.

So, enough code. I am sure your first question when looking at this journal entry was "well, who is that lady in the picture at the top of the page?"

The lady in question is the secret agent character for the game. Half way through the game she replaces the boss at intervals, giving you advice and subtle warnings about your manager's antics. Having enough free time to do so, and wanting to try my hand at drawing once more, i decided to create the concept for this character and start shading it myself.[table][td='33'][/td][td='33'][/td][td='33']secretagent.png[/td][td='33']secretagent-2.png[/td][td='33']secretagent-3.png[/td][/table]

As you can see from the image, i am now about half way through this and all i can say is that creating art takes a lot longer than creating code especially if it doesn't come naturally like it doesn't to me. For me to create reasonable art, i have to spend many tedious hours within GIMP, gently massaging pixels to be where i want them. Time will tell if i am able to create some art passable enough for the game, and of course feedback from anyone more artistically inclinded about my creation is always more than welcome!

On with the show, and back to the grindstone. Please comment and leave feedback if you have any questions!

Keep an eye out for my new article, coming to this site soon: How to automate deployment of your game!

Hi everyone!

Another week has passed, and more features have been added into my game. Today, I have found some time to create the end of level animation.

I have drawn my inspiration heavily from casual mobile games such as angry birds, cut the rope and Bag-It, which all have a sequence of stars at the end of the level which fill depending upon the amount completed.



I also managed to find some very nice sound effects on opengameart.org which have several different variations, each one becoming more excited and louder the more they progress through the sequence. I have used these to ensure that the better you do the more congratulatory the music seems.

Of course, if you don't get the required percentage at all, you get the classic 'game over' music, as in the previous 2D version of the game.

The general concept is to remove the need for the user to interpret percentages - in a short glance of a nice looking animation they have determined if they passed or failed the level and if their next step should be to try again (or perhaps throw their controller at the monitor in frustration maybe?) or advance straight to the next level with a big smile on their face, punching the air in a "YES!" pose.

I have also completed various small bits of polish such as the "picking list" on the bottom right of the screen, which indicates which crates are coming next and what order is correct. As the crates move along this list, the sprite shakes as if under stress and rumbling sounds are played.

Dropping crates off the edges of the playing field are now accompanied by a cartoonish "whee-kersplat" sound effect.

Oddly enough, just these small pieces of polish is making the game seem more like a game as time goes along, and showing non-technical strangers videos of the game in progress now results in "oooh's" and "aah's" as opposed to "what is that? looks like minecraft...".

On a related note i have decided to call the main character "Eric Boom", or "Mr Boom" for short as a result of this thread...

Stay tuned for further updates from the firework factory!
Hi everyone!

So, again, it's been a couple of weeks since my last entry. Moving on from where we were, to where we are now, I am now in a position to start putting together various subsystems and linking them together to form a flowing gameplay experience.

In previous journal entries I have discussed writing a spritefont implementation, and writing various bits and bobs for animation etc.

Today I will cover my event system which I have used to link together the component parts such as the menu, boss (tutorial character and all-round plot device) and the gameplay portion proper. The event system is at its simplest level a base class from which other classes may be derived, named "Action":

class Action{private: // True if the action is currently repeating, e.g. a held key bool keydown; // The name of the action, e.g. "Camera/Up" std::string name;protected: // Creator class FireworkFactoryWindow* creator;public: // Instantiate a new action. Creating an action inserts it into a map // of named actions where it can be located and bound to key presses. Action(FireworkFactoryWindow* parent, const std::string &actionname); // Destructor automatically removes the action from the named actions map ~Action(); // Called when repeated execution of the action is possible, e.g. key held down virtual void ExecuteAction() = 0; // Returns the name of the event std::string GetName(); // Returns true if the key bound to the action is currently held down bool IsPressed(); // Called once when the button bound to the action is first depressed virtual void Pressed(); // Called once when the button bound to the action is finally released virtual void Released();};As Action objects are instantiated, they are inserted into a global std::multimap of Actions, each of which has a sensible name which is used to identify it. Each action may be triggered manually (by locating its object and calling its methods) or by associating it with an input, e.g. a keyboard button or a mouse button. Note that although the methods are called "Pressed()", "Released()", and "ExecuteAction()" they do not neccessarily have to map to a physical input, and derived forms may implement derivation-specific methods which may be useful. For example all Actions who's names start with "Audio/" could contain for example a "SoundEffectFinished()" method...

The general idea of action objects is to disassociate behaviours with the triggers for those behaviours. Many games will directly plug the behaviour of the space bar to the fire action, for example. This is all well and good, until you later come along and want to run a demo sequence, or multiplayer, or perhaps just want to remap keys.

In Firework Factory, a seperate input buffer (fed from the windows message loop thread) populates a buffer of key presses, which are then used to dispatch events to Actions:

* This function handles input coming in from windows message loop, but buffered into our own * buffer to deal with at a time when we want to deal with it. */void FireworkFactoryWindow::Input(double frameTime){ /* We process a maximum of ten events per frame, so that all players have a constant known * input rate for fairness. Else, faster machines respond quicker. */ const UINT32 MaxEventsPerUpdate = 10; UINT32 EventsRead = 0; BufferedKey bk(0, VK_NONAME, KE_NOTHING); Action* k = nullptr; /* Iterate the buffer removing all keypresses that exist and processing them */ do { bk = keybuf.GetNext(); /* Dispatch each different event type to the right method */ switch (bk.GetEvent()) { /* Keyboard key pressed */ case KE_KEYDOWN: { ActionListKeys eventlist = keymap->Get(bk.GetKeycode()); for (auto k = eventlist.first; k != eventlist.second; ++k) { /* Note: We call Action() here too on key press, because if the * key is both pressed and released within the same set of buffered * events, Action() would not be called AT ALL otherwise. This would be * technically wrong. */ k->second->Pressed(); k->second->ExecuteAction(); } } break; /* Keyboard key released */ case KE_KEYUP: { ActionListKeys eventlist = keymap->Get(bk.GetKeycode()); for (auto k = eventlist.first; k != eventlist.second; ++k) k->second->Released(); } break; } EventsRead++; } while (bk.GetEvent() != KE_NOTHING && EventsRead < MaxEventsPerUpdate); /* Iterate all active command handlers triggering the Action() method if their * key is currently held. There is a special case for this above for keys which are * not held long enough to cross two frames. */ keymap->DoActions();}As you can see, all input events are rate limited and tied to a fixed 16ms (70fps) clock, and handled at a set position in the code. This ensures that the input system does not run at different speeds on different PCs. The GameWindow::Input() method used here is called in a similar way to GameWindow::Update() but is seperated purely for readability. Any key may be mapped to multiple actions, and any action name may be claimed by multiple objects. At any time the key mappings may be changed by calling the InputMap class's SetNew() method, or by simply instantiating the InputMap class:

keymap->SetNew(L"Keymaps/Game.txt");There is also a method to insert and remove individual key mapping entries, which is not used any more from the public interface. These keymap text files are stored within the game's main archive file, and are a simple tab seperated list of key name and action name, e.g.

VK_A Camera/LeftTo map the names (e.g. VK_A) to an actual platform specific keycode, I programattically generated a huge header file, which contains the name and platform specific keycode for each possible key, plus a description for the UI:

#include typedef std::pair KeyDetail;typedef std::map KeyNames;KeyNames KeyNameList = KeyNames(boost::assign::map_list_of ("VK_ADD", std::make_pair(VK_ADD, "Numpad +")) ("VK_ATTN", std::make_pair(VK_ATTN, "Attn")) ("VK_BACK", std::make_pair(VK_BACK, "Backspace")) ("VK_CANCEL", std::make_pair(VK_CANCEL, "Break")) ("VK_CLEAR", std::make_pair(VK_CLEAR, "Clear"))[...] ("VK_VOLUME_DOWN", std::make_pair(VK_VOLUME_DOWN, "Volume Down")) ("VK_VOLUME_MUTE", std::make_pair(VK_VOLUME_MUTE, "Volume Mute")) ("VK_VOLUME_UP", std::make_pair(VK_VOLUME_UP, "Volume Up")) ("VK_XBUTTON1", std::make_pair(VK_XBUTTON1, "X Button 1")) ("VK_XBUTTON2", std::make_pair(VK_XBUTTON2, "X Button 2")));We now have a facility where any particular system may be called independently of its association with what the user is doing. We use this for good effect while the menu is on screen. Although the game is paused, we can continually call the Camera/Left event to rotate the playing field below the translucent menu (see the video below for this effect in the flesh):

void MainMenu::Update(double frameTime){ Menu::Update(frameTime); ActionList al = creator->GetInputMap()->Get("Camera/Left"); for (auto it = al.first; it != al.second; ++it) { if (auto cla = dynamic_cast(it->second)) cla->ExecuteAction(); }}The dynamic_cast isn't strictly required in this case, as all instances of Action must implement the ExecuteAction() method. This would be used however in a templated "CallAction" method, to call an action class given it's type and action name. Similarly, we can do the same to trigger a "quit the game" action from anywhere. We could attach a dialog box to this, if we wanted, or perform any other cleanup behaviour such as an autosave, and it would be triggered from anywhere that calls the event:

case MO_QUIT:{ ActionList al = creator->GetInputMap()->Get("Gameplay/Quit"); for (auto it = al.first; it != al.second; ++it) it->second->Pressed();}As promised, here is a video demonstrating the game so far, with all of this humming away quitely in the background. As you can see, it is now appearing much more like a game, and less like a bunch of loosely related parts, our lego kit is coming together to form the whole:



As always, feedback, votes and comments are welcome!
Hi everyone.

So it's been a little while since my last journal entry. Various things about the internals of Firework Factory were annoying me so in true programmer fashion i had to spend some time ripping them out and rewriting them. Refactor, refactor, refactor!

Over the past couple of weeks i have removed every last trace of D3DX from my code, and fully got rid of any dependency upon the July 2010 DirectX SDK, making sure the entire game will build just using the Windows Platform SDK.

This has been a huge success, and now I hopefully have a cleaner, and more future proof codebase. I do not want to end up in the same rut as before, with a game written in a dead technology.

As part of this I had attempted to integrate DirectXTK into the game so that i could use its texture loader, and sprite font capability. This did not all go according to plan. I spent several days trying to identify why whenever i drew fonts with it's implementation of SpriteFont, it would mess up my rasteriser state, drawing just elongated triangles all over the screen. After temporarily moving most of the scene initialisation into per-frame initialisation to troubleshoot i was still having issues, and decided that as a learning experience and to be in full control of the source code, i would write my own SpriteFont implementation on top of my existing Sprite2D implementation, instead of using DirectXTK SpriteFont on top of Texture2D.

I decided that there were some features (font rotation, font scaling etc) i did not need as i could simply do these by drawing my sprites onto a texture and rotating that if i found i needed these facilities later, and that these did not need to be part of the spritefont class itself.

This did not take as long as I had expected, and my own cut-down implementation of SpriteFont was a lot simpler and easier to read than the one that comes as part of DirectXTK.

My Sprite2D implementation is a simple 2D sprite renderer, which uses WIC to load the sprite from a PNG file stored in a zip:class Sprite2D{private: // Direct2D Bitmap ID2D1Bitmap* graphic; int* rawpixels; // Direct2D render target ID2D1RenderTarget* rendertarget; UINT width; UINT height;public: // Create a new Sprite2D from a file on disk using WIC to load the image. // Throws std::exception on failure to load the file. Sprite2D(ID2D1RenderTarget *pRenderTarget, const std::wstring &filename); // Draw the Sprite2D. DX11::Begin2D() must be called before this can be used. void Draw(const D2D1_RECT_F &destination, float opacity, const D2D1_RECT_F &source); // Free bitmap resource ~Sprite2D(); UINT GetWidth(); UINT GetHeight(); UINT GetPixel(UINT x, UINT y);};
The astute amongst you may have noticed the GetPixel() method immediately. Please do not grab your torches and pitchforks and descend upon me just yet - this is not as inefficient in terms of CPU usage as you might first think. Currently only the SpriteFont system uses this (you will see why below) and it references a copy of the actual ID2DBitmap which is copied as raw pixels into an int array.

For the uninitiated, at its basic level a SpriteFont as seen by XNA is a bitmap sprite sheet, with a bunch of metadata to identify where each rectangle is that identifies any one particular glyph. This metadata is built by the content pipeline at compile time.

In my version of SpriteFont i decided to cut out this process of pre-compiling metadata. Identifying the glyphs in the bitmap is quite fast enough to be done when loading the assets, as no player notices a couple of extra milliseconds here. In short, i am able to keep my spritefonts as just a simple sprite sheet and build the rest at runtime.

My sprite sheets for fonts look like this:

[sharedmedia=gallery:images:6121]

This represents each of the ASCII characters from a space (ASCII 32) to the "}" character (ASCII 125). These are loaded using the Sprite2D class above, and then passed to a Font class:// Maximum ASCII value of highest glyph in a sprite sheet, minus one#define MAX_GLYPH_ASCII 126// Representation of a sprite font.// Sprite fonts are read from a sprite sheet generated by an external application// e.g. iron Star Media Fancy Bitmap Font Generator. Usually in XNA these are parsed// as part of the content pipeline into an XML schema, some metadata and the underlying// bitmap. Parsing the bitmap ourselves isn't /that/ expensive these days and perfectly// acceptable to do at initialisation time.class Font{private: // The sprite sheet containing all the glyphs from space to '}' Sprite2D* spritesheet; // The rectangular location in the sprite sheet for each glyph D2D1_RECT_F glyphs[MAX_GLYPH_ASCII]; // Identifies if a given pixel is opaque magenta bool IsMarkerColour(UINT pixel); // The height of a line of text calculated via max(all character heights) UINT lineheight;public: // Constructor accepts a sprite object Font(Sprite2D* sprite); // Draw a string to the screen at given position and opacity void Draw(const std::string &text, D2D1_POINT_2F position, float opacity); // Measure the X and Y size of any given string D2D1_POINT_2F Measure(const std::string &text); // NOTE: Destructor does NOT delete sprite ~Font();};
The Font class constructor then examines the bitmap of the sprite, looking for the edges of the transparent areas surrounded by opaque magenta:// Fully opaque magenta in ARGB formatconst UINT32 MAGIC_MAGENTA = 0xffff00ff;Font::Font(Sprite2D* sprite) : spritesheet(sprite), lineheight(0){ // Start at ASCII value for space char index = ' '; // Clear all glyph entries so they default to a size of 0,0 ZeroMemory(&glyphs, sizeof(D2D1_POINT_2F) * MAX_GLYPH_ASCII); // Heavily based on MakeSpriteFont.cs from DirectXTK... for (UINT y = 0; y < spritesheet->GetHeight(); ++y) { for (UINT x = 0; x < spritesheet->GetWidth(); ++x) { // Look for the top left corner of a character (a pixel that is not the marker colour, but was the marker colour immediately to the left and above it) if (!IsMarkerColour(spritesheet->GetPixel(x, y)) && IsMarkerColour(spritesheet->GetPixel(x - 1, y)) && IsMarkerColour(spritesheet->GetPixel(x, y - 1))) { // Measure the size of this character, first across horizontally then down, pixel by pixel int w = 1, h = 1; while ((x + w < spritesheet->GetWidth() && !IsMarkerColour(spritesheet->GetPixel(x + w, y)))) { w++; } while ((y + h < spritesheet->GetHeight()) && !IsMarkerColour(spritesheet->GetPixel(x, y + h))) { h++; } // Bounds check if (index == MAX_GLYPH_ASCII) throw std::invalid_argument("Too many glyphs found in sprite"); // Store glyph to the array glyphs[index++] = D2D1::RectF(x + 1, y + 1, x + w - 1, y + h - 1); if (h > lineheight) lineheight = h - 1; } } }}bool Font::IsMarkerColour(UINT pixel){ // Magenta is the marker colour between glyphs. // There is normally a 6-pixel padding of marker colour between each glyph. return (pixel == MAGIC_MAGENTA);}
I based this bit of code heavily on MakespriteFont's BitmapImporter.cs, a c# program for building sprite font metadata which comes with DirecXTK.

As you can see if this was done often during the game loop it would be computationally expensive and very much a no-no, but as it stands, done at startup, this is acceptable as the time taken isn't even noticable.

Once this initialisation has run we have an array of rectangular areas, each of which represents the ASCII character glyph for a given ASCII code. We simply iterate a string's characters to draw it, displaying each in turn and iterating the X and Y coordinates to draw the glyphs at appropriately:void Font::Draw(const std::string &text, D2D1_POINT_2F position, float opacity){ // Iterate all characters in the string. Note that characters we have no glyph // for are rendered as a 0 by 0 pixel bitmap so do not appear in the output. for (std::string::const_iterator ch = text.begin(); ch != text.end(); ++ch) { // Skip carriage return if (*ch == '\r') continue; // Newline advances the position down one line using the lineheight else if (*ch == '\n') { position.y += lineheight; continue; } // Calculate width and height of current glyph const D2D1_RECT_F& glyph = glyphs[*ch]; UINT gwidth = glyph.right - glyph.left; UINT gheight = glyph.bottom - glyph.top; // Draw the section of the sprite sheet that refers to the glyph spritesheet->Draw(D2D1::RectF(position.x, position.y, position.x + gwidth, position.y + gheight), opacity, glyph); position.x += gwidth; }}
For measuring the potential dimensions of any particular string in a given font, we can use the same style of function, just with the Draw calls taken out:D2D1_POINT_2F Font::Measure(const std::string &text){ D2D1_POINT_2F ret = D2D1::Point2F(); ret.y = lineheight; // The code here is practically identical to Font::Draw, except instead of drawing // we just record the current X and Y sizes of what we would have rendered. for (std::string::const_iterator ch = text.begin(); ch != text.end(); ++ch) { if (*ch == '\r') continue; else if (*ch == '\n') { ret.y += lineheight; continue; } const D2D1_RECT_F& glyph = glyphs[*ch]; ret.x += (glyph.right - glyph.left); } return ret;}
Now we have a complete font rendering system, we can use it to build a simple menu out of sprites and fonts:

[sharedmedia=gallery:images:6120]

The code to do this then becomes very simple:titlefont->Draw("this is some text", D2D1::Point2F(x_pos, y_pos), 1.0f);
As for potential improvements to this code, i see no reason why i couldn't pre-render the entire string onto a texture or other 2D sprite, then blend that texture or other 2D sprite into the screen when needed. However, for now, this is performant enough for what i need to do, as you can see in the video below of it running on top of the actual game:



Comments and other feedback are always welcome! smile.png
Hi everyone!

I've found some time today to make further progress on Firework Factory, adding three things as mentioned in the title:

  • Added AABB collision detection of boxes and particles, so that the game mechanics now function entirely correctly for crates
  • Added a skybox, to make the game world seem less dull
  • Added a software cursor



    Firstly, the collision detection of boxes was added. This was quite a simple change to make to the source, and luckily AABB checks are not CPU intensive, and even when i have to check against thousands of active particles, it does not seem to be too much of a problem smile.png Admittedly, you don't see many games where particles have to be collision checked with anything more than a plane (raindrops falling on the floor, maybe?) so this is a break from the norm compared to a lot of games that use particles purely as passive effects.

    The skybox, well, I guess this is the technical term but there is nothing related to the sky about it... as you are inside the factory. I added an in-joke here which you will probably get if you are in England. On the right hand wall is a HSE poster, a mandatory statement of health and safety compliance for UK businesses. I've had to doctor out the royal crest in the top left (I don't want any legal trouble for duplicating the crest!) but it just serves as a joke, having a safety compliance notice on the wall of what is supposed to be a very unsafe place by design.

    The game grid still 'floats' off the surface of the factory. I intend for it to always be raised off the floor like a table, and at some point later i may add legs at the bottom or chains hanging it from the roof. The crazy design is of course in no way related to any real life sensible factory.

    The final change this time around is a software cursor. There are two reasons for adding this - firstly, the 2D version of the game had it, and it looked quite nice, being alpha blended and translucent. Secondly, the functions required to load and render the sprite on the screen was something I didn't know how to do in Direct3D 11 until now. Now that i have implemented it, I can easily use this for displaying GUI and menu systems, and the game's HUD which indicates the next crate, etc. The addition of 2D sprites is done via a simple sprite class:/* The Sprite2D class is a wrapper around loading and drawing 2D sprites to a * Direct2D render target, loaded via WIC. */class Sprite2D{private: // Direct2D Bitmap ID2D1Bitmap* graphic; // Direct2D render target ID2D1RenderTarget* rendertarget;public: // Create a new Sprite2D from a file on disk using WIC to load the image. // Throws std::exception on failure to load the file. Sprite2D(ID2D1RenderTarget *pRenderTarget, PCWSTR uri); // Draw the Sprite2D. DX11::Begin2D() must be called before this can be used. void Draw(const D2D1_RECT_F &destination, float opacity, const D2D1_RECT_F &source); // Free bitmap resource ~Sprite2D();};
    The render target drawn to is an independent texture, which is drawn to the screen with translucency over the top of the 3D scene, therefore any GUI elements, menus etc that i draw can always be gauranteed to be on top of the action.

    Next on the list is exactly that: A simple menu. Coming soon to a YouTube video near you...

    As always, comments are welcome and encouraged! smile.png
Hi everyone,

Another day, another journal entry!

Today I have integrated a bunch of disparate parts of the game together, and we are finally reaching the point where all the unconnected parts start to join together to form a playable, complete whole.



The junctions, demonstrated in my second journal entry before Christmas, are now correctly tied into the simulation, as are the machines, demonstrated in my previous blog post, and the level exit, which takes crates away from the playing area. These are all tied together by the Animation class, which can interpolate any object over time, adding rotations, position transformations, and scaling on all three axes.

For now, I spawn new crates for testing purposes by pressing the space bar - this will eventually be read from the level definition. Crates are stored to a std::vector, which is updated every time through the game loop: for (CrateListIter crate = activecrates.begin(); crate != activecrates.end();) { Crate* c = *crate; if (c->Exploded || c->CorrectlyExited) { crate = activecrates.erase(crate); } else { (*crate)->OnUpdate(); ++crate; } }
Whenever a crate explodes (they don't actually do this yet) or correctly exits via the exit, it is removed from play. For now, falling off the edge is treated the same as exploding.

As you can see later in the video, there is no collision detection as of yet - this is possibly next on my to-do list, and along with it comes connecting the particle emitter to the collision, which brings with it effects and other related goodness smile.png

Stay tuned for further updates, and as always, comments are welcome!
Hi all,

Today I have managed to add machines to the level layout. The machines are the producers of the crates in the level, as seen in the video of the 2D version with cute smiley faces on top and flashing lights for a nose.

The new version still has flashing lights for noses, and hopefully at some point i will get around to adding point lights as part of the effect. In the new 3D version i have decided to give the lights square edges so that they can rotate, the movement will hopefully draw some attention.



Technically, this is implemented by grouping together related models (e.g. the light and its machine) into a class called BoxCollection which holds a std::vector of Box objects ("box" being the internal name for a model in the game).

Comments as always are welcome smile.png
Over the past few days i have been working on two things within the game as shown in the YouTube video:



The first thing i have been working on is the input system, which handles buffering and processing of input. After some discussion on the forums, i had come to the conclusion that the following things were needed:

  • The input system needed to seperate the incoming messages from the game loop, so they could be processed on the game's terms, not immediately
  • The input system needed to be able to rate limit the number of events per second, so that people with faster or slower PCs did not have an unfair advantage and that the game played the same everywhere.
  • The system needed to retain a buffer from which events could be read

    For the most part this new input system does exactly what it set out to do - the unfortunate side effect of an input subsystem is that it isn't something you can show to people who have an interest in your game. People making games like ourselves will look at the code in the thread above, understand what is going off and understand that a lot of time and effort has gone into it, and it has been made better. Normal users however, game fans... Let's just say they would be quite unimpressed.

    With this in mind i also set out to properly load the level files which should be automatically loaded and parsed from disk when the game loads.

    The level files are simple text based files, which will eventually be stored within a "pak" style archive. Each of them has a set number of instructions as shown below:VerticalConveyors 3 7 10HorizontalConveyors 3 7 9Machine 3 0Machine 10 0CrateExit 7 11OutSequence 0 mortar 1OutSequence 1 mortar 5OutSequence 1 dynamite 1CorrectOrder mortar dynamite mortarIntroTextStartWelcome to the firework factory! I am your new boss, Mr Seafor. For your first task,simply move those firework crates off the production line. BE very careful not to break any...They are a little ... volatile!IntroTextEndEndTextStartWell, all looks in order here! Congratulations on a job well done, maybe I should give you apromotion to something a little more challenging!EndTextEndFirstCrateSeconds 4TimeLimitSecs 60Music 1Pass 10
    The VerticalConveyors and HorizontalConveyors fields indicate at which X and Y columns and rows there will be a horizontal or vertical conveyor belt. The game automatically places junctions at the intersections where they meet.
    The Machine lines indicate the X,Y 2D coordinates of a machine on the playing field, from which crates are emitted.
    The OutSequence lines indicate which types of crates come out of which machines and when, and the CorrectOrder line indicates what order the crates are expected to reach the exit. The IntroText and EndText are quite self explainatory (shown at the start and end of the level, spoken by the 'boss' character). FirstCrateSeconds indicates how long the game waits at the start of the level before producing the first crate.

    I load these with a simple text file parser, which places them into a quite straightforward object:class OutSequence{private: int machine; std::string cratetype; int nextcratesecs;public: OutSequence(int mach, const std::string &crate, int nextcrate); int GetMachine(); const std::string& GetCrateType(); int GetNextCrateSecs();};class Level{public: // Position of vertical conveyor belts in the level layout std::vector VerticalConveyors; // Position of horizontal conveyor belts in the level layout std::vector HorizontalConveyors; // Positions of machines on the end of conveyor belts std::vector Machines; // Crate exit point - there can only be one in the level D2D1_POINT_2U CrateExit; // Output sequence of crates from the machines std::vector Sequence; // The expected correct order of the machines through the exit std::vector CorrectOrder; // Intro text spoken by the boss before the level begins std::string IntroText; // End of level text spoken by the boss on successful completion std::string EndText; // How long the player gets before the first crate is produced int FirstCrateSeconds; // Time limit to complete the level int TimeLimitSeconds; // Music file to play as background music std::string Music; // Percentage completion needed to pass int PassPercent; // Parses an on-disk level file Level(const std::wstring &levelfile, const int levelnum);};
    These are then placed into a std::map where the key is the level number and the value is the pointer to the Level object:LevelManager::LevelManager(){ const FileList levs = Util::ReadDirectory(Util::GetExePath() + L"\\Levels\\*.lvl"); for (FileListIter f = levs.begin(); f != levs.end(); f++) { std::wstring filename = Util::GetFileName(*f); int level = _wtoi(filename.replace(filename.find(L".lvl"), filename.length(), L"").c_str()); levels[level] = new Level(*f, level); }}
    Once all the levels are loaded, the system simply draws the level according to this layout, and produces the crates as specified in the configuration. These levels are entirely data driven, and a new level can be created by simply dropping a new numbered file within a directory.

    Some of you may be asking why i didn't just define a two-dimensional array for the level layout, as this would be what you would usually see for a game design of this type. There are a few answers to this:

    Firstly, the levels are adapted from the level design of the first game, which also did not use an array of sprite types. Due to wanting to re-use the data and designs, I decided to keep the same layout.

    Secondly, designing the level in this way means that there is less chance for an error in level design. It is not possible to place a junction where there is no conveyor, or place a conveyor that ends abruptly without exiting properly, or anything of that nature. The system smartly places the conveyors and junctions just based on the one position.

    As i plan to eventually let users design their own levels and publish them online, this is an important factor, the simplier i can make the level editor, the better as I want this to be something any gamer can just pick up the game and do, not someone aspiring to make games who might already be a bit technically minded.

    As you can also see from the YouTube video i have added the animation now for the crate exit. Unlike in the 2D version of the game, where the crates simply dissapeared on contact with the exit, in the 3D version of the game, the exit transports the crate away off-screen, accellerating quite rapidly, then returning. The idea is that any crates left waiting at the exit whilst the transport plate is "away" will queue up in a line, which should add a bit to the challenge and add an interesting gameplay mechanic.

    Next on the list to code is dispatching of crates, and crate movement around the level.

    As always, comments welcome! smile.png
So, over the past week i have been busy implementing the first of many different particle effects for the game.

A video of the particle effect can be found here on my YouTube channel.

The whole of the game hinges around pretty (and rather deadly) particle effects, so this is an important part of the design. The first of these effects is a particle "fountain" much like a roman candle firework. The effect is implemented as a combination of a pixel and geometry shader, with instancing used to display many thousands of identical quads each containing a simple texture:

sparkle.png

This texture is used to build up the effect, with each particle having a pseudo-random velocity. Over time each particle is pulled back to earth via a gravity effect, which causes the outwards momentum to drop by 75% and the upwards momentum to be replaced slowly by downwards momentum as shown in the code below.[code=nocode:0]void ParticleSystem::Update(){ for (int i = 0; i < activeParticles; i++) { particles.pos.y += particles.yvelocity; particles.pos.x += particles.xvelocity; particles.pos.z += particles.zvelocity; particles.yvelocity -= particles.falloff; if (particles.zvelocity > particles.initialzvelocity / 6.0f) particles.zvelocity -= particles.falloff; if (particles.xvelocity > particles.initialxvelocity / 6.0f) particles.xvelocity -= particles.falloff; }}
In the game proper, each of these particles will be destructive to any crates they come into contact with so it is unlikely that i will be outputting so many particles during gameplay or this may make the game far too hard.

At present the system does not free particles which have been allocated, as this is for testing only - once i have finished this portion of the game the textures which are allocated will be freed back into a pool (with a maximum of 10,000 at a time) to be displayed when needed.

The particle effect seems to run quite well at over 100 frames per second, so long as fraps doesnt get loaded as fraps does not seem to like my laptop one bit.

Now that we have a basic firework particle effect, i will add the crate movement next and conveyor belts, which are also just as fundamental to the basic gameplay as the particle effect!

Stay tuned for further updates coming soon!
So, after a week of development here are the videos of the progress so far in the 3D version of Firework Factory!

A youtube version of this video can be found here.

As you can see there is now an animation system to allow for simple animation of blocks on the playing field. The example here is the simple junction box, which can be used to change the direction of travel for each of the firework crates.

As far as graphics goes, i just have to implement machines, crates, and the particle system and all basics will then be implemented.

The Animation system works by attaching an 'animation' derived class to the Box object, and then calling the DoUpdate() method of each animation object once per frame. Constructors of the Animation derived classes contain the basic instructions required to perform the animation, as shown below:[code=nocode:0]JunctionAnimation::JunctionAnimation(Box* b) : Animation(b){ instructions += AniInstruction(0.0f, 0.01f, 0.0f, 0.f, 0.0f, 0.0f, 10), // Raise up AniInstruction(0.0f, 0.0f, 0.0f, 0.f, 2.0f, 0.0f, 45), // Rotate 90 degrees AniInstruction(0.0f, -0.01f, 0.0f, 0.f, 0.0f, 0.0f, 10); // Sink down}JunctionBox::JunctionBox(float _x, float _y, float _z, float _rotation, DX11* dx11) : Box(_x, _y, _z, _rotation, dx11->textures[L"step1.png"], dx11), anim(NULL){}void JunctionBox::OnUpdate(){ if (anim) { anim->Update(); if (anim->IsFinished()) { delete anim; anim = NULL; } }}void JunctionBox::OnClick(const POINT &p, float pickedDist, const XMVECTOR &prwsPos, const XMVECTOR &prwsDir){ if (anim == NULL) { anim = new JunctionAnimation(this); }}JunctionBox::~JunctionBox(){ if (anim) { delete anim; anim = NULL; }}This uses boost::assign to make things nice and simple and easy to read smile.png

Next on the todo list: Particle effects for the crate explosions! I probably can't re-use much of the 2D particle system i created before, so wish me luck!
My current project is a puzzle game called "Firework Factory".

This project has been sitting on my hard disk since 2010, however i haven't actively worked on it since late 2011. I started out with the concept of producing a puzzle game with a plot - looking into the puzzle games currently available very few do, they just have a long sequence of "solve this" and "solve that, now a bit harder".

The idea of the game is to get boxes of fireworks from one end of the factory to the other, re-ordering them in the expected order displayed on the screen. Doing so is made possible by the fact that the player controls various "junctions" which can be made to redirect crates in any of four directions, but must be changed manually. Sending crates down the wrong path, on a collision course, results in pretty particle based explosions, each particle being deadly to nearby crates. Getting the crates out of the factory in the correct order as soon as possible adds to your percentage completion, each level having a minimum percentage to complete (think "cut the rope" or any other game where you must complete each level to a satisfactory standard to unlock the next). As you advance through the levels, plot is revealed to you which changes the game, sometimes subtly and other times drastically. I will not ruin the plot by posting spoilers here, but suffice it to say it is quite interesting, if i do say so myself - if you really want to know the plot in full and are not concerned about spoilers feel free to PM me!

Well, as they say: "All best laid plans of mice and men". When i started on this project, I was single and living with my parents. This changed in 2012 as i settled down with my now wife and we had two babies together. This is not a situation which lends itself to free time to develop games, so the project was put on hold.

Fast forward to November 2014, and as the kids are a little older and my job has changed to one where i am finishing work earlier, and able to take breaks whenever i wish so long as the work is complete, finding myself with a little free time to get back into game development.

Dusting the e-Dust off the repository, I dig around in my code familiarising myself with it once more. After a few minutes of research i find my worst fears confirmed - Microsoft have decided to abandon XNA, there will be no XNA 5, and what's worse, it will not be supported in any form on the Xbox One. This leaves my project in free-fall, not being sure what to do next.

I weigh up the options - move to monogame, rewrite the project in something more future-proof, or just give up. Giving up not being even on the cards for me, after investing so many months producing the game (and coming so close to finishing too) i explore the other two options.

Currently, monogame does not support the content pipeline, so a straight conversion of the project files automatically is out of the question. I could rewrite all the bits of the project which don't work right in monogame, and copy and paste the rest, or i could wait for the newer version of monogame which will (i have been assured) support the content pipeline.

Being the impatient type, i decide against waiting for a new version of monogame - i have free time NOW, and i want to develop my game now, waiting is not an option as free time is few and far between for me.
Also, looking at what Microsoft did to XNA, and the fact that it will not exist on the xbox one indicate to me that targetting the xbox 360 is no longer a sane option for me - it is last generation's hardware.

So, this leaves me with option three - a rewrite. Let's be honest here, most developers when given old code look for an excuse to completely rewrite it as soon as possible, just because it isn't their code. Look honestly at yourself and try to tell yourself this isn't true, deep down smile.png ...

So, starting out on the rewrite i settle on Direct3D 11. Changing the game to a 3D game has several advantages, which are:

  • Most gamers will be quicker to accept a 3D game as "new" and not something "their dad played when he was a kid" - let's be honest, most gamers are materialistic and are wowed by graphics and presentation, not gameplay.
  • It is more future proof - the DirectX platform has been around since 1995, and isn't going to vanish next year.
  • Changing the view of the game indirectly makes it a little more difficult which increases the challenge - the 3D camera slightly obstructs the view, causing the player to consider more about what is going off in the game and assume more about the things he can't currently see.
  • DirectX 11 will just be interesting to learn - in the end, does there need to be any other reason than this? happy.png

    So, this explains why I've settled on rewriting my game in DirectX 11 (and by extension changing to C++, which i was always more comfortable with than C# anyway!) and changing it to a 3D view.

    On to progress - so far, i have implemented a simple camera, based upon a third person camera which is always looking to the centre of the playing field. The player may zoom in or out, and rotate around the playing area a full 360 degrees. When the player zooms in or out, the pitch of the camera adjusts so they view the playing field from a slightly raised angle, from above, to give a clearer view of all the playing field. As they zoom out the camera goes flat, closer to the level of the boxes on the playing area.

    I have implemented a way to render the conveyor belts, and the boxes. This will give me the basis of a playable game soon enough, once i port all my existing game logic from C# to C++ (an easy, if not a bit of a time consuming task!).

    I have also created a simple audio facility within the game which integrates with fmod (which is now, i might add, free to Indies with budgets of less than $100k... nice).

    For artwork, i have realised that i can render 99% of the graphics using bump mapped, shaded and textured cubes, and scaled variants thereof, and a simple particle engine (see the project screenshots to see what i mean regarding this - everything is basically square - easy!) which means that for the time being, i do not need any 3D modellers, and i can re-use a lot of my existing artwork.

    So, i will now end my first journal entry, with a promise of a simple YouTube video upload at some point in the coming week. Comments as always are welcome cool.png
Sign in to follow this  
  • Advertisement