Sorry your browser is not supported!

You are using an outdated browser that does not support modern web technologies, in order to use this site please update to a new browser.

Browsers supported include Chrome, FireFox, Safari, Opera, Internet Explorer 10+ or Microsoft Edge.

AppGameKit/AppGameKit Studio Showcase / Making Desert Of Gold

Author
Message
DewarInversion
3
Years of Service
User Offline
Joined: 27th Mar 2021
Location:
Posted: 1st Feb 2022 14:20 Edited at: 7th Feb 2022 18:32
The Game Setup

DoG is a tower defence game with story line elements with open buildable world. I started my research with a good modern TD game called X-Morphefense. The game has both air and ground units and central character ship. This ship is player controlled and can pick up debris to use as a resource to build more towers. I knew I wanted to implement a resource collection element into my game.
But first, there is the design work to do, so I started out creating turrets and some enemies.

With these turret idea's and some notepad scribble's I imported the content into Studio and straight away animated them, choosing random angles and spinning at different speeds. The road was made from simple blocks of which there are nine elements to represent the connections. These were dragged and dropped by hand onto the map. I then created a grid element which is a three vector variable myGrid[1,x,y]. The one denotes level. The grid also has a type assigned to it which contains information like column and row, if there is a tower placed there etc. The grid I limited to 50x50. With the basic grid made it was time to add some enemies. This I drew up with textures and shapes generated in filter forge. I also clamped their size to the same size as a grid element (I chose 124x124). I then set to work on a way point following system.

Waypoint Following

I tried two types of wp system, one where by the enemy moves a sprite with position dependent on time and direction, the other system (the one which I chose for the game) I made from physics based movement using sprite velocity commands (shown here).



With the first waypoint system I wanted to quickly add enemy routes. I started with a .scence parser which looked at each image in the scene and entered into myGrid[1,x,y].isPath what it found there. So if there was a road, the grid would get an integer isPath = 1. If it was an empty turret space it got myGrid[1,x,y].isTurretPlacement = 1. For the enemy wave point I would place a waypoint maker sprite so when the parser found it, the grid ref would be entered as waypoints into a waypoint list. It soon became apparent that this method created far too much work with a high degree of possibility of things going wrong. So I searched for a solution and found an algorithm called Dijkstra, which is suitable for all kinds of wp code, including basic grid square like in my game. I converted the pseudo code I had into AppGameKit Tier 1 and ran some tests. There were some, lets say, surprisingly funny results. Now I had a simple function which generates a list of way points for any object to follow.


Path Finding

Dijkstra is in fact an early version of A*. The algorithm length is half that of A* and serves to work better with equal grids like in my game. You can find more about about Dijkstra here: https://forum.thegamecreators.com/thread/228195

You can see the path-finding Dijkstra working in DoG by setting GAME_MODE to 1 and ROUTEFINDING_VISUALS to 1
This path-finding algorithm is good at finding point to point along a grid (we call this information a graph:a list of nodes which are possible travel routes). It is also good at finding the closest of multiple grid references. I use both these systems in the algorithm and added this into a single function which can receive a command as to which mode to use (point to point or point to closest).



The mode string is just a conditional if looking for the type of node being searched. For example, for my collector to search for some junk (and not the foundry) the mode set is ‘Junk’ then Dijkstra will look for



Once the junk is collected, the collector needs to return to the foundry with it, so the mode is set to ‘Normal’ with start and end nodes as input.Dijkstra then looks for



As you can see, because Dijkstra is run for every trip, if the path layout changes (Player can build new roads) any new path layout taken into graph() will be valid, as long as myGrid[1,x,y] is kept up to date. Because the grid size is only 50x50 the graph update is relatively straightforward.



Note how this function is looking for information in both the target node and the adjacent nodes. This is important information for both the AIroutefinding and for the path laying functions. I store this important information in type called ‘pathConnections’. This is the list of adjacent nodes which have a path and is saved inside each node. I also store here what type of path it is with node.graphText. This can then be printed on screen for bug hunting. As you can see, the determination for processing the node for list inclusion is the conditional



This is just one of many data types kept in myGrid and is found using a parser function.For example, to find an empty turret placement I use:



Knowing what nodes are adjacent and if they are a path or not is useful information. The junk placed in locations can be on both a road or on the ground. I didn’t want the collectors to leave the road so I would need a way for the collector to get at the junk without leaving the road. This is now easy to do, because every node knows if adjacent nodes are a path or not. So I made a new type inside myGrid[] called JunkNode. So the conditional looks like this:



So, all I am doing here is finding grid nodes which have more than zero junk and setting every adjacent node (Horizontal, vertical only)to 1. But, because I want to know which of these junkNodes are on a path, I need to cross reference them with the isPath node data. I create a new type called junkNodeOnPath and run this very simple loop:



Now, when the collector routefinding runs, and I want it to search for ‘Junk’ it looks for the conditional isGraphJunkNode = 1. This conditional stays where it is for as long as there is junk in the adjacent node. When the junk is deleted (after collection) then isGraphJunkNode = -1 and so gets ignored by the route finder.
When the collector needs to return with it’s loaded goods, it does a search with the route finding function like this:



tempAIstart is the current collector position, tempAIend is the node inside the foundry where I want to deliver the load, and the mode ‘Normal’ says I want to find the end node and not go looking for junk nodes. Route finding for the enemy units is the same ‘Normal’ mode with their start at the screen edge and the foundry as the end node.

Collector Programming

The collector now can make it’s own way point list by calling the pathFinding() function. But what causes it to enter the correct mode? The collector has its own function I have called collectorProgram(). The collectors use a very simple state logic. A collector can be in one of many states which tell it what it should be doing. So when I create a collector I initialise like so:



This is a static mode where the way point list is empty and the ‘Junk’ mode pathfinder is invoked. If the path finding is successful, the return list of way points from the route finder is entered into the collectors list and the collector changes state to:



The collector stays in this mode, following it’s way point nodes contently checking to see where it is along the route. The condition it looks for is:



Where does this mode$ come form? When I create the path finding list, I not only add the nodes to this list, I also add information as to what this node is, then store it in .mode$ like so:



So the collector can look at this information at anytime to see what the waypointType$ is and take action accordingly. It’s like a coded trigger which gets fired when the conditional is met:



It’s useful have this separation of mode$ because I may want to use different information for different units should the game be expanded. I can just add to this list and make the collector ‘watch’ for the mode to appear. The collector is effectively reacting to the way point mode$ dependent on it’s state, in this case heading to a JunkNodeOnPath. Now the collector is next to a junkNode it can load up with materials and move on to the next state:



You can see here, the collector has a decision to make. If there was another collector which was closer, it may have already picked everything up (called a race condition). Or it could have picked up nearly everything. Or maybe it is the first to arrive and the junk pile is full. So the next state for the collector is going to be determined by what it finds at the end of it’s way point list. It can not mind read other collectors, it has to go look!
Also, I do not want to look for the junkPile in the collector current position but in the node adjacent to the road (where the junk is physically located). I do this by using a node type call junkNodeFocus. This justs points to the node which has the junk so the collector program can delete from that node and not from the collectors current position (collecting adjacent rule).
In “RETURN” mode the collector now calls the path finder for a route back to the foundry. Once with the way point list inside the collector the state then changes to “RETURN_FOLLOW” and so returns back along the way point list until it gets to the foundry, unloads and changes state back to “SEEK”. So we see from this the collector states follow a very simple program pattern. “SEEK” “SEEK_FOLLOW” “RETURN or SEEK” “RETURN_FOLLOW or SEEK_FOLLOW”.
Before leaving the collector program let us look at two more collector states. One is invoked by the user, where by a button is pressed and all the collectors return to the foundry to hide from the enemy. We need to somehow interrupt the collectors routine and invoke a new state. The button code looks like this.



And the code for the button using a toggle



The programMode is changed by the scheduler like this:



Two things in this code are important to note. I have not directly manipulated the collector state. I have simply made the request by setting a flag in myEngine.sendCollectorToFoundry = 1. Why have I done this? If you take a look at what is going on, the request gets picked up by the collector and is processed immediately. But the “SEND_FOUNDRY” is only one of three states it needs to carry out this task. I want the collector to route find to the foundry “SEND_FOUNDRY” then “SEND_FOUNDRY_FOLLOW” where the collector follows it’s way point list back to the foundry, and then I want it to wait there “SLEEP”. So these three collector states are triggered by this single conditional myEngine.sendCollectorsToFoundry = 1.
The code to pole user button and change collector state is thus:



And the collector then carries on with it’s assigned roll. Using states for object control is easy and expandable. I was able to add a new state I called “WAIT_FOR_PROGRAM”. This was needed because during testing, if all the junk on the map had been collected, the collector program spammed the routeFinding function on each and every frame. With ten collectors doing the same, the game locked up (was very slow). This program mode was easy to implement with the following code.



The routeFinding() function is programmed to return -1 if a route cannot be found. The collector just checks this and switches mode depending the function return value. The “WAIT_FOR_PROGRAM” is simple enough. We just put the collector back in “SEEK” after a set time. If the collector finds some junk (The user may have added a new road) the collector goes back on its assigned “SEEK_FOLLOW”, if the result is again -1 then the collector stays in “WAIT_FOR_PROGRAM” waiting for the next “SEEK” state.

Linked Lists

I wanted to add a story into the game. For this I would use text and a character portrait. I modified some code from AppGameKit to animate the letters. Inputing text in amongst code was ugly and awkward to work with. So the text strings were written in Scriverner, exported to a text.txt file and loaded into the game at runtime. Each text string I gave a number and the program called the character dialogue with a simple function call.



I also wanted to give the player some option choice as part of the function. This is invoked by adding a 1 for a simple ‘I’ve read that’ affordance with white buttons and the choice selection buttons which are red and green. The function then returns a value and this value is dependent on which choice the player made (Cselect).



This is working code example how I implemented the character text function. ‘Cselect = characterMessage(69,2)’ calls the function asking for text ID 69, which is the text talking about the MG3 upgrade. The code also passes the value 2 to tell the function it is expecting the user to make a choice and have the function make the buttons green and red. By colouring the buttons I am telling the user this is a choice message.



The code extract is held in a case select statement, case 73. This is part of the dialogue system I designed for DoG. The components parts to this system are:
1.A list of indexed character strings which is the text to display in an external text.txt file.
2.myPlayBookOpen list which provides information on where the case listing is connected to.
3.myPlayBookClosed a list with information on case listings which have been accessed during gameplay.
4.A time stamp feature to record when the listing is entered into the Openlist (Important for time based case statements.)
5.A setUpgrade(STRING,val) call to tell the game engine the state of the research icons in the foundry.
6.’Result’ from the case select statement so the calling function ‘PlayBookGateWay()’ knows what to do when the function returns. (default 0, nothing is done)
7.Access to the ‘broadcast’ system so game functions can talk to each other.

The way this system works is very simple, infact the code for the PlayBookGateWay() is very short.



All we are doing here is sending one item from myPlaybookListOpen to the playBookEntryProgram(). I programmed for the playBookId and the case statements to be the same. When PlayBookEntryProgram() finds a conditional in the select, it tests the condition, carries out the contents and then returns true or false. In some case statements there are new listings to be added to myPlayBookListOpen. So in this way listings can appear latter after other events have been triggered. Another type of conditional will look at the time stamp, compare it with the current time and if the condition time has passed it runs the case code.
The beauty of using linked lists is the conditionals only get tested when the contents are ready to run. In the same way as the route finding AI, linked lists need to have at minimum one entry in the open list. The first in my game is shown here.



This open listing points to the select case = 2 via the commandFunctionSwitchID. Case 2 is a time Stamped conditional and looks like this:


As you can see in the code this case statement is doing two things. The first is the character statement 10. The other thing it does is adds an new entry into the open list, linking these two lists together. When PlayBookEntryProgram() returns to the gateway, it returns a result = 1. The gateway then adds the listing it sent to the closed list then deletes this list from the open list. This case will no long be checked for conditionals. Removing a listing ensures we do not see the same message twice. Some conditionals are sticky. This is easily done by removing the result = 1 and the open listing remains. So to ensure the player can get the same message from the upgrade icons clicks we return result = 0 and the gateWay will keep this conditional on the open list.
The upgrade icons can be accessed too. Here I have the input function wait for a click on an icon with the following:



If you look at the case 73 code shown above, you will see the conditional: if myEngine.UpIconClicked = "MG3". I’ve also done some cheating here. Icons can only be green after they have been researched. So if the icon clicked on is green then I display a message saying ‘you already have this’. If the icon isn’t green then I set myEngine.UpIconClicked = “MG3” and PlayBook will take over.

This system works well, but I found tracing conditionals took longer the bigger my game got. At the time of writing I have 152 message statements with 134 conditional statements in the playBookEntryProgram(). The bigger it gets, the longer the debug time (and mouse wheel scrolls). It can get horrendously knotted in places and there is no visual indication for open ends, broken links, links which have no entry point (logical lockout) links which get reinstated unintentionally and entries being run even when it is undesirable to do so. Clearly I have some work to do with this system, but the basic principles of the linked list works, just as long as there is no editing to do. Hopefully, by the time you read this I will have found a solution.

Turrets

A tower defence game needs something attacking and something defending. In my game, the turrets are defending the foundry and the enemy units try to get past the turrets. I would have different types of turrets. These also can be upgraded too. At the time of writing, there are only two turret types in the game. The Machine Gun turret, fast firing, small damage and the Cannon, slow firing and high damage.
How these turrets looked and how they moved was an early entry into the game. I tried different looks until I came up with the final designs. The graphics I made modular so the animations could be put together. To animate the turrets I used spriter Pro and gave them two animations mode. Static, which has no animation, and firing, a sequence where the animations show the gun firing and the shells reloading. In early version of the game I loaded in the turrets with .scon files. These worked well and looked good. Also I could make small changes in spriter and then instantly run the game with the new look.
In later editions of the game I moved the turret animations from .scon files to .png atlas files and animated using AGK’s sprite animations features. This is so easy to do I now wonder why I didn’t do this in the start. Spriter can output an atlas with parameters set for my target animations quality. The smallest animation is the level 1 MG turret with only 8 frames and atlas 244x524.



In early code experiments I had four bullet sprites placed onto one turret sprite which could all rotate. However, putting everything into one sprite atlas is better in work flow and in implementing code (sprites can have physics add, .scon files can’t). Offsets were easier to find and modifications could be done offline without having to alter any code.



The MG turret with amobox and twin turrets came with an atlas 695x1038 and 35 frames. The byte size of this file is 262kb which isn’t very big by todays standards. I was also careful not to allow too much alpha pixels, especially if they didn’t and anything looks wise. I also had a sprite atlas for the turret mode when it wasn’t firing and the fan blade rotating. This added sprite sheet didn’t offer anything special to the game, and the turret looked cool when it fired so in the end I used just the one sprite sheet.
The double barrel cannon with mounted loader is the biggest sprite sheet in the game.



For the animations to look smooth it runs at 60 frames a second and runs for 100 frames. With the cannon set inside a sprite animation, I could now query the current frame and play the cannon firing sound in sync with the animation. Admittedly, I had so much fun with spriter it was a challenge to get back into the coding. In the image show, the sprites are assigned ‘bones’ and it is these bones which move relative to the parent bone. Also, the z-order determines the sprite depth so I ordered the shells to look like they were inside the box. Parts of the barrel is obscured by the turret mount proper and the disk on top rotates as the cannon reloads. All three animations from turret cannon level one to turret cannon level 3 are all in the one spriter file. If you use the .scone file, these animations are easily accessed using commands.

Targeting

I now had turrets and enemies. Time to put the two together to make a turret game. This was my first tryout with AGK’s excellent physics system and I found a command which makes a sprite a sensor, that is, this sprite is still part of the physics engine but it doesn’t react when things bump into it, but it does get registered as a collision. So above every turret I made a circle sprite, set it to invisible and a sensor and gave it a name. If I wanted to know if an enemy is inside this circle I call the physics engine to tell me. The overhead for checking this very small as the physics engine keeps a note of all the objects. The test for collision is only making a request to physics engine data. The first turret tracking system used a target list for each turret. It became apparent, the code I had written wasn’t very fast (passing list by reference is very slow and prone to me getting confused on where the list came from). The code I adopted doesn’t make a list. The targeting code locks on to the first enemy it finds within target rang and doesn’t look for a new target until the enemy leaves the target radius or is made dead. In a recent build, the turret is in such a hurry to kill the enemy it fires even before it is locked on.
Before the turret fires, it first needs to find the angle relative to the turret and the enemy. AppGameKit comes with a neat fast little function.



By entering the turret position and the enemy position in the x,y chords, the return is the angle in degrees to the target. I’m sure this function has something to do with trigonometry but I am a game writer not a mathematician so I have no idea how it works, it just does.
I have now two angles. One is the angle the turret is currently pointing, the other is the angle to the target. There are different techniques we can deploy here. I used the tween method initially. Later I had some code ‘donated’ to me based on curve calculations which quickly became my favourite method. All I do is enter in the numbers and I get a heading angle for the turret to point at. This is a very useful function.



The last part of the turret code needs to fire at the enemy. To do this I needed three things to happen. I need to set the fire animations going. I need to make a sprite appear for the muzzle flash(and make it disappear) and for the cannon I needed to eject a casing. To make the casing eject was real easy.



This is code for make a physics object and then punch it real hard. I had real fun with this one, and left the funniest one in the game.

Muzzle Flash Animation

For the turrets to look more real when firing I added sprite animations which are played in the turrets rotation. The muzzle flash animations is also a sprite sheet and this time I used a new program called PixelPart (Steam download).
This program is simply awesome. By creating particles and combining them in different ways I can create a sprite sheet. At all times during the production I can preview how the final animation will look. Settings for example the sprite frame size is shown during the development stage. Additional particles can be added in and the settings change easily. At a frame rate of 60 and to a scale of 124 (my typical enemy size) the sprite animations look real enough. The explosions have four atlas sheets which are chosen at random and AGK’s excellent handling of sprite and animations make the process fast any easy. After establishing a simple work flow with sprite sheets (see turrets earlier) it was clear to me I wouldn’t be needing to add particles to a game like this. My coding time and processor allocation can then be pushed towards the AI side of things knowing the graphics are going to look great after the animation process is done. There will be times where a sprite sheet probably wouldn’t look good and particles will be necessary, but in practice, most people wouldn’t see the explosion as four different animations played over and over. The enemy shields, barriers, smoke and fire are all products created in pixelpart and I am delighted with the results and even more delighted with the minimal effort needed to get them into the game.



The image here shows four particle systems stacked on on top of another. Each particle element has force, motion colour and spawn settings which I tweeked until it looked good. This program would also be a great tool pixel games. This is anther program I needed to drag myself from as I would end up with a disk of sprites and no game.
I mentioned earlier one of the reasons I switched to sprites is the ease at which frames can be animated in sequence with sounds. A good example of this in use is the dual cannon firing. To sinc the sprite to the sound all that is needed is a few lines of code.



I have so much more to write, but I am mindful this post is getting too long. So if you have read this far then well done!
DoG isn’t finished, Albertine Savary tells me her adventure has only just started so there is to be lots more content added as your journey across the desert unfolds. But for me, I’m going to get a coffee.
You can down load Desert Of Gold on itch.io here: https://dewarinversion.itch.io/desert-of-gold
The full game release date is yet to be decided, all updates will go to itch.io. Please feel free to leave comments.

Attachments

Login to view attachments
Virtual Nomad
Moderator
18
Years of Service
User Offline
Joined: 14th Dec 2005
Location: SF Bay Area, USA
Posted: 1st Feb 2022 15:39
Great read; will re-read at least a couple more times.

Thanks for the insight into DoG's creation. As you know, i've beaten the game but the next time i go through it, i'll have an enhanced perspective
DewarInversion
3
Years of Service
User Offline
Joined: 27th Mar 2021
Location:
Posted: 9th Feb 2022 19:39 Edited at: 13th Feb 2022 22:43
Here's a quick update on DoG.
I've been working with load and save files in .json format with agks brilliant saving feature. I have also created an app which can load DoG config files, edit them and then save them. The game will then use this config file if it exists. If it it doesn't exist, it will revert to defaults.

The config file gives the player the ability to switching on visuals. These include:
1. print list box detailing important game stats including turrets, enemies and collector AI
2. a visual representation of Dijkstra at work for both enemies and Collector AI
3. a visual representation showing AI search for the closest road for a tower
4. access to multiple game stats including enemy hitpoints, tower fire rates, max collectors etc.
5 access to exclusive game modes

mode 0 : the basic story game and program default
mode 1: access to all upgrade research with all ingame towers unlocked
mode 2: same as above with only MG and canon1 available
mode 3: alterative mode 1 with constant enemy waves increasing in intensity.


The upload for this new version are now available (edit)
https://dewarinversion.itch.io/desert-of-gold

Attachments

Login to view attachments
DewarInversion
3
Years of Service
User Offline
Joined: 27th Mar 2021
Location:
Posted: 20th Feb 2022 13:00 Edited at: 20th Feb 2022 13:01

Login to post a reply

Server time is: 2024-11-21 12:45:39
Your offset time is: 2024-11-21 12:45:39