BACHELOR THESIS
Andrej Ciˇzm´arikˇ
Framework for Development of Tower Defense-like Games
Department of Software and Computer Science Education
Supervisor of the bachelor thesis: Mgr. Jakub Gemrot Study programme: Computer Science Study branch: Programming and Software Systems
Prague 2017 I declare that I carried out this bachelor thesis independently, and only with the cited sources, literature and other professional sources. I understand that my work relates to the rights and obligations under the Act No. 121/2000 Sb., the Copyright Act, as amended, in particular the fact that the Charles University has the right to conclude a license agreement on the use of this work as a school work pursuant to Section 60 subsection 1 of the Copyright Act.
In ...... date ...... signature of the author
i N´azevpr´ace:Framework pro tvorbu her typu tower defense
Autor: Andrej Ciˇzm´arikˇ
Katedra: Katedra softwaru a v´yukyinformatiky
Vedouc´ı bakal´aˇrsk´epr´ace: Mgr. Jakub Gemrot, Katedra softwaru a v´yuky informatiky
Abstrakt: Tower Defense hry s´upopul´arnym subˇz´anromreal-time strategick´ych hier, kde hr´aˇc chr´ani str´aˇzen´y objekt obrann´ymi veˇzami pred n´ajazdmi nepriatel’ov. S´uˇcasn´e Tower Defense hry s´uvˇsak uzavret´e, ˇc´ım znemoˇzˇnuj´u ak´ukol’vek rozˇs´ıritel’nost’ a sk´umaniezdrojov´ehok´odu. Ciel’om pr´aceje preto vytvorit’ multiplatformov´yframework pre tvorbu hier typu Tower Defense spolu s ilustraˇcnouimplement´aciouhry v tomto frameworku. Framework podporuje potrebn´e hern´e objekty: veˇze, jednotky, ako aj interakcie medzi nimi – vˇsetko skriptovatel’n´ev jazyku LUA. Dalejˇ podporuje manipul´acius text´urami, anim´aciami,zvukov´ymiefektami a hern´ymimapami vytvoren´ymiv dostupnom grafickom editore. Uˇz´ıvatel’sk´erozhranie sa navrhuje v deklarat´ıvnom jazyku XAML. Ciel’ pr´acesa podarilo naplnit’ – pomocou vytvoren´ehoframeworku je moˇzn´evyv´ıjat’ Tower Defense hry na platforme Windows a Android.
Kl´ıˇcov´aslova: framework, v´yvoj poˇc´ıtaˇcov´ych her, Tower Defense
Title: Framework for Development of Tower Defense-like Games
Author: Andrej Ciˇzm´arikˇ
Department: Department of Software and Computer Science Education
Supervisor: Mgr. Jakub Gemrot, Department of Software and Computer Science Education
Abstract: Tower Defense is a popular subgenre or real-time strategy, where the player guards an object by building defending towers against raiding enemy units. Current Tower Defense games have, however, closed source code which completely prevents any possibility to extend these games or study their code. Due to this fact the aim of this thesis is to develop a multi-platform framework for development of Tower Defense games together with an illustrative example game created using the framework. Framework supports game objects: towers, units and interactions between them – all scriptable in scripting language LUA. Framework also supports textures, animations, sound effects and game maps created using an available graphical editor. User interface is designed using declarative language XAML. In the result we fulfilled the aim of the thesis – using the created framework users can develop Tower Defense games for platforms Windows and Android.
Keywords: framework, video games development, Tower Defense
ii I would like to thank my supervisor, Mgr. Jakub Gemrot, for his patient guidance and valuable advice he has provided during my work on this thesis. I am also very grateful for the support my family provided not only during the work on this thesis but throughout my whole bachelor studies.
iii Contents
Introduction 3
1 Problem analysis 5 1.1 Tower Defense definition ...... 5 1.2 Game mechanics in Tower Defense ...... 6 1.3 Our definition for Tower Defense-like ...... 8 1.3.1 Supported mechanics ...... 8 1.4 User interface layouts ...... 9 1.5 Creating and representing maps ...... 9 1.6 Framework requirements ...... 10 1.6.1 Pluggable design ...... 10 1.6.2 Target platforms ...... 10 1.6.3 Third party libraries ...... 11 1.6.4 Graphics library ...... 11 1.6.5 User interface library ...... 12 1.6.6 LUA interpretter library ...... 13 1.7 Supported features ...... 13
2 User documentation 15 2.1 Target audience ...... 15 2.2 Creating a new game ...... 15 2.3 Content Pipeline ...... 17 2.4 Adding graphics and sounds ...... 18 2.5 Adding unit animations ...... 18 2.6 Adding visual effects ...... 19 2.7 Adding units ...... 19 2.8 Adding towers ...... 20 2.9 Adding game actions ...... 21 2.10 Scripting support ...... 22 2.10.1 Debugging embedded scripts ...... 22 2.11 Scripting game entities ...... 23 2.12 Scripting units ...... 25 2.13 Scripting towers ...... 28 2.14 Scripting game actions ...... 30 2.15 Registering for scripting ...... 31 2.16 Adding upgrades ...... 32 2.17 Adding maps ...... 32 2.18 Adding levels ...... 34 2.19 Adding sounds ...... 36 2.20 Adding custom fonts ...... 36 2.21 Adding user interface screens ...... 37 2.21.1 Visual Studio designer tool ...... 39 2.22 Example game ...... 39 2.22.1 User interface in the example game ...... 41
1 3 Development documentation 43 3.1 Development tools ...... 43 3.2 Top-down solution design ...... 43 3.2.1 Solution structure ...... 43 3.2.2 Model-view-viewmodel pattern ...... 44 3.3 Overview of the solution architecture ...... 44 3.4 Game real-time progression ...... 45 3.4.1 Timestep modes in MonoGame ...... 45 3.4.2 Game loop in MonoGame ...... 46 3.5 Framework initialization phase ...... 47 3.6 Framework gameplay phase ...... 48 3.7 The main framework projects ...... 49 3.7.1 Game objects component ...... 50 3.7.2 Input/Output component ...... 52 3.7.3 Data and Persistent storage component ...... 54 3.7.4 Sound system component ...... 54 3.7.5 Spritesheet packing component ...... 55 3.7.6 Level state, Game events and World component ...... 55 3.7.7 User interface and other framework components ...... 57 3.7.8 Custom file format ...... 58 3.7.9 Scripting environment ...... 59 3.8 UserInterface.csproj ...... 60 3.9 GameUILibrary.csproj ...... 60 3.10 MapDataExtractor utility ...... 62 3.11 Expanding the project ...... 63
Conclusion 65
Bibliography 67
List of Figures 70
Attachments 71 A Framework solution and documentation ...... 71 B MapDataExtractor utility solution ...... 71 C Compiling and running framework ...... 71 D Licenses ...... 71
2 Introduction
Tower Defense (TD) is a popular real-time strategy game subgenre, focusing on correct tower positioning and resource management. The main objective is to defend an object against raiding enemy units. TD games are played on maps with one or more paths leading to the guarded object. Players can then build defending towers alongside the paths to destroy and obstruct enemy units from reaching the object. TD games are usually level-based, meaning that players need to successfully finish previous levels in order to unlock harder ones. While TD games have been quite popular over the past years, there are rarely seen solutions which present tools specifically designed to simplify the development of TD games. There are some open-source real-time strategy games [1], however, these games do not serve as a tool and thus can not be directly used to create new games. Even though many successful implementations of TD games already exist, for instance, Kingdom Rush [2], Bloons TD [3] or Plants vs Zombies [4] their source code is closed. This fact completely prevents the possibility of code studying or extending these games. Majority of current game frameworks (e.g., Unity [5], MonoGame [6], Unreal Engine 4 [7] present complex solutions containing many unnecessary features for the development of TD games. Furthermore, many subsystems and game objects, which are actually needed for the development, are not implemented. As a result of mentioned reasons, the goal of this thesis is to design and implement a 2D framework for the development of TD-like games. An integral part of the thesis is implementation of an illustrative game created using the framework as a proof of concept. The framework together with the game provide a decent starting point for creating an easily extendable and multi-platform game.
Project goals
In this section we will list our goals that we aim to fulfill in this thesis.
(G1) Easy way of defining game objects and adding content By simple to use, we mean the user is not required to be a programmer. Game objects should be defined by modifying game files, instead of writing source code.
(G2) Maps The project should support an intuitive way of creating game maps. By intuitive we mean making use of a graphical editor, rather than forcing the user to define complex maps using text files.
(G3) Pluggable design In order to avoid recompiling whole project every time something is changed, we intend to provide a solution relying on the pluggability.
3 (G4) Support for replays The players should be offered with an option to save the replay of the current level. This feature simplifies the process of generating gameplay videos.
(G5) Multi-platform By multi-platform we mean that the project should support development for at least two different platforms.
(G6) Tutorials Publishing a framework without illustrative tutorials may result in discouraging many potential users. Therefore, this thesis should provide easy-to-follow tutorials, showing users how to create their own games.
(G7) Example game As a proof of concept, but also to showcase the features of the framework, a simple TD game should be implemented.
Structure of this thesis
In the first chapter we will analyze the TD genre, focusing on the key concepts and game mechanics found in some popular TD games. We analyze possible approaches for implementation of our framework and establish more specific goals which have to be met during the development. User documentation, consisting mainly of illustrative tutorials is contained in the second chapter. This serves as the starting point for users that want to create their own TD game mostly by modifying the example game. The tutorials illustrate how to configure our framework in order to create games. The third chapter is dedicated to the development documentation of our framework – this part is about implementation in C# programming language. The documentation provides information about the solution design, its architecture and description of the key components with their implementation. Finally we discuss the results of the thesis and mention possible options for future work.
4 1. Problem analysis
We start this chapter by further describing TD genre together with game mechanics in some popular TD games. From this summary we continue by defining what has to be supported in our framework. Later on we discuss different possibilities of designing and implementing some parts of the framework.
1.1 Tower Defense definition
TD as a subgenre of real-time strategy, usually does not progress in turns. This means that continuous time progression is an important aspect of many TD games. TD games are played on maps with one or multiple paths as well as some places to build towers. Enemy units use these paths in order to reach the guarded object. An example of a non-trivial system of paths can be seen in the Figure 1.1.
First path
End of paths
Level status
Second path
Towers
Friendly units
Special powers
Map entrance with enemy units
Figure 1.1: One of levels from Kingdom Rush. Enemy units are raiding from the entrance at the bottom of the screen, trying to reach the path’s end at the top. Source: https:// www.kingdomrush.com/
The most essential part in TD games is building defending towers. Players can usually choose from multiple types of towers to build, where each of them
5 can be better for a different situation. While some towers are totally ineffective against some units, they can be really strong against others. This fact enforces the use of appropriate towers for a given situation as well as taking an advantage of correct tower positioning. The main objective in TD games is then to eliminate all units or, in case of an infinite level, withstand as long as possible. Level-based design ofmostTD games provide further possibilities to present more features, such as upgrades persistent through levels or integrating a storyline.
1.2 Game mechanics in Tower Defense
For this analysis we decided to choose some of the popular TD games, based on the total number of downloads on Google Play Store [8]. We decided to choose following games: Kingdom Rush, Plants vs Zombies and Bloons TD. Each of the chosen games, at the time of writing this thesis, has at least one million down- loads.
Health points: When players are unable to stop an unit from reaching the guarded object, some of the health points are lost. This mechanic was present in every analyzed game for tracking the win-condition.
Resources: In all analyzed games the resources can be used to perform var- ious tasks, for example, building a tower, upgrading a tower or using a special power. Players gain resources after destroying an enemy unit. The amount of gained resources corresponds to the enemy difficulty. Additionally, Plants vs Zombies offers towers which generate resources over time.
Unit types: The games deploy various unit types. Besides graphical appear- ance, units may differ in their properties but also perform different actions. This ensures that the game is not monotonous and requires different strategies based on currently attacking units.
Tower types: Similarly, having various unit types, the games provide differ- ent towers as well. Towers are used for automatically attacking raiding enemy units. However, they can perform other actions, such as applying various effects, or generating resources.
Tower upgrades: Whenever players build a basic tower, they might be of- fered with an option to upgrade it. These upgrades are active only for the duration of the current level. This can be seen in Kingdom Rush and Bloons TD.
Persistent upgrades: Persistent upgrades after purchasing usually remain active for the rest of the game, which means they are active for more than a single level. The games we analyzed implement upgrades as technology trees [9].
Tower placement: Some TD games are played on a grid-based map, as seen in the Figure 1.2. This means that each map is divided into tiles where players
6 Towers
Resources Obstacles
Enemy units
Figure 1.2: An example of a grid-based TD game - Plants vs Zombies. The game map is divided into tiles on which can be built towers. The players are able to build certain towers which serve as obstacles. The enemy units are then forced to destroy them as they are blocking their path. Source: http:// www.popcap.com/ build their towers. Other TD games are played on maps with no particular struc- turing and players can place towers only on few designated places. This can be seen in Figure 1.1.
Waves of enemies: Waves serve as an abstraction for multiple enemy units attacking at once. Waves tend to get stronger over time, which is generally achieved by deploying stronger units and attacking from multiple paths.
Friendly units: Concept of friendly units, as seen in Kingdom Rush, turned out to be quite popular. By this mechanic we understand placement of friendly units on a path providing additional obstructions for enemy units. Whenever enemy units get into the range of friendly units they engage in combat.
Time controlling: When designing levels, it is sometimes necessary to leave a brief moment for a player between waves to prepare for upcoming attacks. As we believe that being forced to wait for a game to spawn next wave can be con- sidered annoying, players should be provided with an option to skip this time.
Obstacles: In certain TD games it is possible to place towers in such a way that they create obstacles for enemy unit. This usually results in the enemy units
7 being forced to either destroy the tower, as seen in Plants vs Zombies, or to take detours. This kind of TD games are usually played on grid-based maps.
Paths: Some TD games contain maps having only one path. Other TD games like Kingdom Rush provide multiple entrances and paths for enemy units to choose from. These paths at some point usually join into one path which con- tains defending object at its end.
Special powers: In some TD games players can further interact with the world by using special powers. The special powers can be various actions and spells for players to use on either friendly or enemy entities. This mechanic can be seen in multiple TD games, for example, in Kingdom Rush and Bloons TD.
1.3 Our definition for Tower Defense-like
To describe what should be supported in our framework, we are going to evaluate the described game mechanics. This will give us a better example on what should be supported regarding the TD genre.
1.3.1 Supported mechanics To create a good TD framework, it would be very convenient to support all mechanics found in popular TD games. However, this decision would result in our topic being too broad to reasonably cover in this thesis. Therefore, according to the previous game mechanics descriptions, we are setting the requirements on our framework as follows:
Health points and resources: Our framework has support for health points to track win-condition. It also supports at least one basic resource.
Unit and tower types: Our framework has an easy-to-use option to define new game objects including units and towers.
Tower and persistent upgrades: Similarly, to defining new objects, users can define upgrades of towers. Our framework provides support for persistent upgrades between levels.
Tower placement and paths: Our framework supports maps with multiple paths, entrances and exits. Towers can be placed only on designated places.
Special powers and friendly units: Our framework supports associating actions with user interface controls, this can be used to implement special pow- ers. Friendly units can be spawned by towers, behaving as described in section 1.2.
Waves of enemies: Our framework has an option when defining levels to split events into waves.
8 Time controlling: Our framework has an option to skip to next event or wave.
Obstacles, in a way they are described, will not be supported by our framework. All paths are static and do not change during any level.
1.4 User interface layouts
Based on characteristics of TD games, we are going to describe possible user interface layouts. The aim of this analysis is to provide better API to interact with user interface and integrate these constructs into the example game.
Main menu: Almost every game requires at least a simple main menu. These screens usually contain multiple buttons – each of them can take us to a different screen or perform certain actions.
Level selection: We already mentioned that majority of TD games are level- based. Therefore our framework should provide API to support operations, such as mark a level, show next or show previous levels.
Heads-up display: During a level players should be able to access most of the user interface controls from the same screen they are playing on. This requires the framework to be able to show user interface screens as an overlay over the actual game.
In-game menu: An useful construct for creating, for example, a pause menu can be an in-game menu. This can be either fully visible menu or hidden until a specific event occurs.
1.5 Creating and representing maps
We already defined that games created using our framework are played on maps with no particular partitioning. Therefore there may be multiple roads, each having its own entrance and exit. Towers can be placed only on designated places and may spawn additional friendly units.
Paths: Each path can be represented as an ordered list of waypoints, where each waypoint is a point in two-dimensional space. Starting from the entrance in a way that each two points with neighbouring indexes form an edge in the path.
Towers The places designated to build towers on as well as spawnpoints can be represented as points in two-dimensional space. The connection between a spawnpoint and a tower can be represented as an edge between these points.
From this analysis we can clearly see that the map representation is really simple. To keep the creation process of maps simple as well, we can let users draw maps – to embed the data directly into map textures.
9 We still need to solve the transformation process into the described representation. This might be hard to achieve if we allowed users to use raster graphics. However vector graphics seems to be a suitable solution because the map data can be easily extracted when using one of the popular formats, for example SVG [10]. As stated in goal (G2) we need to provide a graphical editor and since we want vector graphics we could generally use any editor capable to work with vector graphics. However we believe that InkScape [11] is one of the most popular editors and therefore the users might be familiar with this software. Users can then draw paths, places to build towers and spawnpoints using certain primitives provided by InkScape. Our framework should provide a simple tool which is responsible of transforming the data from vector graphics into the described representation.
1.6 Framework requirements
In this section, we will discuss third party libraries, which we need for certain tasks during framework implementation. Regarding graphics capabilities of our framework, we are only going to implement support for 2D graphics. Creating 3D models and textures requires additional tools and certainly more time compared to 2D graphics. To keep the framework simple to use, we are going to be working with 2D graphics only.
1.6.1 Pluggable design We need to represent behaviors of entities and actions between them. Furthermore, apart from the correct representation, we need to make this plugable so that when altering behaviors we do not need to recompile the whole project. For this type of behavior representation we considered two following options:
• Behavior definition in a markup language [12]
• Integrating scripting language [13]
As we intend to provide more flexible solution, where the user is not limited by given document structure, we decided to integrate an existing scripting language. Using this solution the user is able to store object states as well as use advanced flow control structures. We decided to choose LUA [14] because we believe it’s one of the most used scripting languages for game development. Therefore this approach should be appealing for many game developers.
1.6.2 Target platforms We decided to support development for platforms Windows and Android. While Windows being the most popular platform on desktops and Android the most popular platform for mobile devices, we believe that this way our framework can target most of the current devices.
10 1.6.3 Third party libraries The next thing we need to do is to choose additional third party libraries. As implementing various technical solutions, e.g., graphics and sound libraries, UI library and LUA scripts interpreter, would be topic on their own. Due to this fact we need to choose libraries for the following aspects:
• Displaying of real-time 2D graphics and sound playback capabilities
• Creation and integrating of user interface
• LUA scripts interpreter
In addition, each chosen library needs to be multi-platform so that games created by our framework will not be limited to a single platform as well. The minimum requirement is that they support at least development for Windows and Android.
1.6.4 Graphics library The first step is to find a graphical library. This decision is going to narrowour options regarding other libraries and tools used during the development. Apart from already mentioned requirements we also need:
• Game loop [15] support
• Sprite batch operations support
• User interface support
• Good documentation and tutorials available
SharpDX [16] is an open-source MIT licensed .NET wrapper of DirectX [17]. This library provides low-level API for development on platforms with DirectX support. This fact is quite limiting because Android does not support DirectX.
OpenTK [18] is an open-source MIT/X11 licensed .NET wrapper of OpenGL [19]. Similarly to SharpDX this library provides low-level API for development on platforms with OpenGL support. In addition this library provides support for game window, which implements game loop pattern and few other useful features for game development.
MonoGame is an open-source Microsoft Public licensed game framework. This library serves as a wrapper under the same API for both DirectX and OpenGL [19] – thus gaining the ability to switch the underlaying technology at any time based on the targeted platform. The framework implements inter- face of discontinued framework XNA [20] which makes it possible to also use some tutorials and examples created for XNA. The support for game development is broader compared to the libraries above, for example, support for fonts, media player and a tool for managing content.
11 Unity is a proprietary licensed game engine. Similarly to other game engines (e.g., Unreal Engine 4, Wave Engine [21]), Unity provides a complex solution with various tools, features and editors. Despite the many advantages of game engines, using any of them would result in unnecessarily complicated and bloated solution which is not intended, as we are creating a rather simple framework.
SharpDX OpenTK MonoGame Unity Sprite batch operations Audio support Game loop support User interface - - - Pluggable design - - - - Documentation, examples Limited Limited Windows platform Android platform - License MIT MIT/X11 MS Public Proprietary
Even though Unity seems to fit our requirements better than other described alternatives, its license is rather limiting for us – the terms of use may possibly change during the development and its source code is closed. Therefore the use of this library could have a negative impact on our framework. Due to these reasons we decided to choose MonoGame library. The decision determined us to use .NET framework for the further development, more specifically we are going to implement the framework in C# programming language. We can still ensure Android support using Xamarin’s Mono compiler [22]. In the following two sections we will describe possible additional libraries to provide the support for user interface and pluggable design because these two features are not supported by MonoGame itself.
1.6.5 User interface library Choosing a library for user interface turned out to be quite challenging. Due to the fact that there are few libraries suitable for our framework we could not meet all our requirements. Let us list the requirements first:
• Support for controls needed in game development (image button, heads up display etc..)
• Support for scaling according to device’s DPI [23]
• Good documentation and tutorials available
GeonBit.UI [24] is an open-source MIT licensed library. The library provides an interesting solution for creating user interface together with an illustrative tu- torial on how to use the library. However, at the time of writing this thesis, there is not an official support for Android or touch-screen devices in general.
EmptyKeys [25] is a MIT licensed library with some parts being open-source. The library supports development for multiple platforms including Windows and
12 Android. User interface is written in XAML [26] without code-behind support and thus relying on MVVM pattern [27]. Being heavily inspired by WPF, many constructs and components are implemented in a similar manner. However, the library lacks more advanced tutorials and examples on how to properly use the library. Despite the disadvantage, at the time of writing this thesis, EmptyKeys was certainly the best available library for this task.
1.6.6 LUA interpretter library In this section we will briefly discuss options for LUA scripts interpretter on .NET platform. Our requirements on this library are as follows: • Support for most LUA constructs • Possibility to debug embedded scripts in our framework • Good documentation and tutorials available NLua [28] is an MIT licensed library. Even though the library seems to provide working solution for our framework, there is only one available tutorial on how to use it. API for debugging of embedded scripts is present, however it seems to be very limited – certainly not providing an out-of-box working solution.
MoonSharp [29] is an open-source BSD licensed library. This library tries to provide almost the same support as defined in LUA 5.2 language definition [30]. Together with numerous tutorials found on the website and possibility to debug embedded scripts using installed addon to Visual Studio Code [31], this library fits our requirements.
1.7 Supported features
This section provides an overview of the high-level features which will be sup- ported by our framework. This overview had later an impact on the architecture of our project as well as on its implementation. Having already established our project goals, we will link the goals (G1) to (G5) with their realization in our framework.
(S1) Easy way of defining game objects: From the goal (G1), we know that an easy way to define game objects is required. All game objects are defined using textual game data files. (S2) Adding content: Content supported by MonoGame is managed using its utility called Pipeline tool. (S3) Editor for maps: To fulfill the goal (G2), maps for our framework can be created using a third party software called InkScape. The program is a freeware distributed under the GNU General Public License. Users can define paths, places to build towers and spawnpoints for friendly units using only the basic primitives. For better maintenance of the map data files, it is possible to split the map definition into multiple files.
13 (S4) Scripting support: Pluggable design as defined in the goal (G3) is achieved with scripting. Our framework provides scripting interfaces with high-level API for easier implementation of the behaviors for units, towers and game actions.
(S5) Sounds support: From within the scripting environment it is possible to play sound effects. Our framework implements a sound system capable of tracking information about played sounds.
(S6) Spritesheets packing: To ensure better performance for drawing, all graphics content needed for the current level is packed into one or multiple bigger textures.
(S7) Animations support: Our framework supports animating units. Anima- tions support can be significantly extended with visual effects which provide more advanced API for animations. Games created using our framework are graphical 2D games represented using bitmaps. Because of this fact we use spritesheets for animations. Spine animations are not supported because we neither have many nor complex animations. One of the reasons is also the fact that good spine animation tools are generally not distributed under a free license.
(S8) Visual effects support: Visual effects are basically more advanced animations, providing the feature to fix to a game entity. The animation is then drawn over the game entity and it changes its drawing position according to the position of the game entity.
(S9) Persistent upgrades: Users are able to define persistent upgrades similar to other game objects. Combined with the replays feature we also need to track used upgrades for each replay in order to be able to display the saved gameplay correctly.
(S10) Graphical user interface support: Users are able to create graphical user interface for our framework to be able to interact with players.
(S11) Support for replays: Replays are required by the goal (G4) to provide an easier way to generate gameplay videos.
(S12) Development for Android and Windows: We know that according to the goal (G5) our framework needs to support the development for at least two different platforms. As we already mentioned our framework supports building games for both Windows and Android platform.
14 2. User documentation
This chapter is mostly dedicated to tutorials on how to correctly use our framework. The tutorials are illustrated on the example game created as a part of this thesis.
2.1 Target audience
We expect the users, in order to be able to fully use our framework, to be familiar with following tasks:
• Working with textual files – editing using pre-defined templates
• Working with bitmap and vector graphics
• Scripting in LUA
• optional: Programming using C#
While programming in C# is not required, it is certainly helpful to understand at least basic concepts as the scripting API as well as our framework is implemented in this language.
2.2 Creating a new game
The architecture of our framework expects the user to copy the whole solution inside his workspace before starting to work on a new game . Information about the solution can be found in the attachment A. This section is then going to serve as a guide providing information on how to configure the framework in order to create a new game.
Now let us have a look at the Figure 1.1 from the first chapter again. Using the same figure, which displays a level from the game Kingdom Rush, weare going to illustrate the motivation for individual tutorials in this chapter. It is also important to mention that the tutorials were inspired by the same game. The process itself can be split into a few tasks. While it is generally possible to perform these tasks in an arbitrary order, it is necessary to reference only already defined and added game objects. Referencing undefined or not correctly added objects will result in errors. We are also going to link the high-level features defined in the section 1.7 with their corresponding tutorials, which describe how to use these features.
Adding units and towers: Each unit and tower after their successful definition can be used repeatedly in every level. The exact approach onhow to define them can be seen in the section 2.7 for units and in the section 2.8for towers. In the tutorials above we cover the process of adding certain game objects in a way described in the framework’s feature (S1). However, this also applies for
15 other game objects like levels, game actions, visual effects and upgrades which will be covered in other tutorials.
Adding maps: Each map consists of two files – one of the files is the actual texture we want to draw on the screen, while the other file contains information about paths, places to build towers and spawnpoints. In the section 2.17 we can see the step-by-step solution on how to create maps. Exactly as defined in the feature (S3) our framework expects the maps tobe created using an editor with graphical user interface called Inkscape.
Adding levels: Each level must have its associated definition file which defines various information about the level. However, the most important part is its list of timed events, which are executed on their time of occurrence. A tutorial dedicated to creating levels with an example can be found in the section 2.18. Based on the information provided about each level, our framework builds spritesheets out of required textures. This is an automatic process which ensures better performance of graphics card and covers the framework’s feature (S6).
Adding game actions: Game actions can be added in a way described in the section 2.9. They provide a way to define various interactions between mul- tiple game entities or player and game entities.
Scripting: To implement new game mechanics and behaviors for game entities we use scripting. Scripting API overview for game entities can be seen in section 2.11. More specific API with scripts examples can be seen in section 2.12 for units and in section 2.13 for towers. Regarding scripting for game actions we can refer to the section 2.14. In the tutorials mentioned above we covered the framework’s feature (S4) about scripting support. Some additional information is also provided in the next part about registering certain objects for scripting.
Registering for scripting: This is an important but very straight-forward process of making game objects, sounds and visual effects available for the script- ing environment. The exact steps can be found in the section 2.15.
Adding graphics, sounds and fonts: All graphics, sounds and fonts are managed by the Pipeline tool provided by MonoGame framework.. An exact approach on how to correctly add content to our framework is documented in section 2.4. Moreover for performance reasons MonoGame requires additional information about fonts which can be generated as seen in section 2.20. These tutorials cover the feature (S2) about adding content, as well as the feature (S5) about the sound support. API regarding the sound support is cov- ered in tutorials about scripting.
Adding animations and visual effects: Each animation is only a sequence of frames. Visual effect is then more advanced animation, which provides additional API for scripting – like timing or fixing on a position. Adding this into framework is described in the section 2.5 for animations and in the section
16 2.6 for visual effects. Therefore this covers the feature (S7) about bitmap animations for game enti- ties and the feature (S8), which requires visual effects with broader options than basic animations.
Adding upgrades: Upgrades are basically binary flags. Their state is accessible within the scripting interface which makes it possible to use upgrades when writing behaviors for game entities. The process of working with upgrades is described in the section 2.16. As upgrades are ensured by our framework to be persistent, this tutorial cov- ers the feature (S9) about persistent upgrades.
Creating user interface: Users can create user interface screens using the Designer tool provided by Visual Studio. The process of their implementation is described in the section 2.21. This basically covers the framework’s feature (S10) regarding graphical user interface support.
Replays are handled automatically – framework tracks needed events and saves them on player’s demand. GameService implements all necessary methods to provide view-model with capabilities to work with replays. This covers the framework’s feature (S11). When the game is ready to be built, as stated in the feature (S12), we can build the game for both platforms Windows and Android. The exact approach on how to compile solution and build for both mentioned platforms is covered in the attachment C of this thesis.
2.3 Content Pipeline
For managing content we use a tool provided by MonoGame framework - Pipeline tool. Added content must follow the predefined folder structure. This means that all graphics content is in the folder sprites, all fonts are in the folder fonts and all audio is in the folder sounds. Pipeline tool then compiles all the content into the custom XNB format [32]. Built content can be referenced in our framework. These are currently supported file formats for building:
• Graphics: .bmp, .dds, .dib, .hdr, .jpg, .pfm, .png, .ppm, .tga
• Audio: .xap, .wma, .mp3, .wav
• Fonts: .spritefont
If the supported formats are for some reason not satisfiable, there is an option to implement custom content processors to expand the Pipeline tool. However, it is advised to primarily use lossless file formats to avoid possible artefacts which may arise during the build process.
17 2.4 Adding graphics and sounds
Graphics and sounds are added into our framework using the previously described Pipeline tool. After the content is either copied or linked to Pipeline tool, we choose the Build option from the menu. After the build process is finished the content is ready to use in our framework.
2.5 Adding unit animations
Animations are added to framework as separate frames in the same way as any other graphics content. Frames of an animation, in order to be identified, need to have consistent filenames. Filenames of the frames have to consist ofa prefix which is common for all frames and an index number. The index numberis a formatted integer to 4 digits with leading zeros. The first frame has the index number equal to 0 and is growing with more frames. An example of adding a new animation can be seen in the Figure 2.1.
# ------# This file contains definition for animations | # (name) - [string] unit name | # (animation_type) - [string] animation type | # (frames_prefix) - [string] common prefix for all frames | # (frames_count) - [integer] count of all frames | # | # Template for definition: | # (name);(animation_type);(frames_prefix);(frames_count) | # ------knight;GOING_UP;knight_run_n;8
Figure 2.1: Example of file animations.tas
In the presented example we can see the definition of an animation for the unit with name knight. The animation type is set to GOING UP. Common prefix for filenames of frames is knight run n and the total count of all frames is 8. The filenames which match the definition from the example are following: knight run n0000, knight run n0001, knight run n0002, knight run n0003, knight run n0004, knight run n0005, knight run n0006, knight run n0007
Framework has some hard-coded animation types, which are automatically switched based on given context. This does not affect, however, visual effects which are basically unlimited. The supported animation types for units are the following ones:
GOING UP, GOING DOWN, GOING LEFT, GOING RIGHT CASTING UP, CASTING DOWN, CASTING LEFT, CASTING RIGHT
18 ATTACKING UP, ATTACKING DOWN, ATTACKING LEFT, ATTACKING RIGHT
2.6 Adding visual effects
Visual effects are added in a similar way compared to the animations. Toadd a visual effects for later use in our framework, we need to add their definition to the file effects.tas, one visual effect per line. An example of the file can beseen in the Figure 2.2.
# ------# This file contains definition for effects | # (name) - [string] unique name of the effect | # (frames_prefix) - [string] common prefix for all frames | # (frames_count) - [int] count of frames | # | # Template for definition: | # (name);(frame_prefix);(frame_count) | # ------fxFireball;fireball;20
Figure 2.2: Example of file effects.tas
In this example we can see the definition of a new effect with name fxFireball. Filenames of frames have the same common prefix of fireball with the total count of 20 frames. The process of fixing on a game entity and modifying timing is resolved within the scripting environment which is covered in tutorials about scripting.
2.7 Adding units
For the framework to know which units are present in the game, we need to define them first. Units are defined in thefile troops.tas, one unit per line. An example of the file with a unit definition can be seen in the Figure 2.3. In the presented example we defined a new unit with name knight. This unit has 20 hitpoints and can travel at the speed of 15 pixels a second – this is mea- sured in the rendering resolution because in the result everything is scaled based on the actual presentation resolution. When this unit is killed players receives 10 resources. This unit is down-scaled to 0.6 of its original size.
The next step is to associate our unit with graphics content. To achieve this we add animations for the new unit as we previously described. Every unit does not need to have associated all animation types, only those which are later used in its script.
The last step to create an unit is to write its LUA script. The name of the script is same as unit’s name. The exact approach on how to write scripts will
19 # ------# This file contains definition and basic attributes for units | # (name) - [string] unique name of the unit | # (hitpoints) - [integer] representing health of the unit | # (speed) - [integer] determines speed of the unit | # (reward) - [integer] reward for killing unit | # (size) - [float] scale of units (default = 1.0) | # (party) - [string] affiliation: FRIENDLY or ENEMY | # | # Template for definition: | # (name);(hitpoints);(speed);(reward);(size);(party) | # ------knight;20;15;10;0.6;ENEMY
Figure 2.3: Example of file troop.tas be described in the section 2.11.
2.8 Adding towers
All towers in order to be registered by our framework need to be first defined in the file towers.tas, one tower per line. An example of the file with definitions of two towers can be seen in the Figure 2.4.
# ------# This file contains definition for all towers | # (name) - [string] unique name of the tower | # (cost) - [integer] price of the tower | # (proj) - [string] name of a defined projectile | # (dmg) - [integer] damage dealth by the projectile | # (speed) - [float] speed of the projectile | # (range) - [integer] range at which tower is able to attack | # (cd) - [integer] projectile cooldown in milliseconds | # (upg) - [string] name of a defined tower or NULL | # (ucost) - [integer] cost of the upgrade | # | # Template for definition: | # (name);(cost);(proj);(dmg);(speed);(range);(cd);(upg);(ucost) | # ------tower_1;50;projectile_1;3;400;110;800;tower_2;100 tower_2;250;projectile_1;10;400;150;600;NULL;0
Figure 2.4: Example of file towers.tas
In the presented example we can observe the definition of a tower with name tower 1 which costs 50 resources. Projectile used by the tower is called projectile 1, which deals 3 damage and firing at the speed of 400 pixels per second.
20 The tower can target all enemies in radius of 110 pixels but fire only once per 800 milliseconds. Once again mentioned speed and radius is measured in the rendering resolutions. On the actual device everything is scaled accordingly. This tower can be upgraded to a tower with name tower 2 after supplying 100 resources. In the definition of the tower with name tower 2 we can see that this tower has enhanced some of its properties and can not be further upgraded.
Next thing we need to do is to define projectiles used by the towers and associate the towers and projectiles with graphics content. This is achieved by altering the file staticAssets.tas. An example of this file can be seen on the example in the Figure 2.5. # ------# This file contains information about static assets | # (name) - [string] unique name of the tower | # (tTexture) - [string] filename with tower texture | # (pTexture) - [string] filename with projectile texture | # | # Template for definition: | # (name);(tTexture);(pTexture) | # ------tower_1;towerTexture;projectileTexture
Figure 2.5: Example of file staticAssets.tas
In the example we can see the association of the tower with name tower 1 and a texture with filename towerTexture. As for the displaying of its projectiles, a texture with projectileTexture will be used.
The last step we need to make is to implement LUA script which contains behavior for this tower. The filename of the script is same as the tower’s name. The exact approach on ho to write scripts for game entities will be described in section 2.11.
2.9 Adding game actions
All game action in order to be recognized by our framework need to be first defined in the file actions.tas. An example of this file with definitions of three game actions can be seen in the Figure 2.6. In the presented example we can see the definition of three game actions. We will now further describe the definitions in the example: The first game action with name heal has cooldown time equal to 1500 milliseconds, casting time time equal to 750 milliseconds, can be casted at range 150 pixels. It targets a single unit which needs to be friendly to the game entity casting the spell. In case the game action is used as a special power, the targeted game entity is friendly to the player. The second game action has name fireball. Its cooldown time is 20000 milliseconds and casting time 0 milliseconds. This game action targets multiple
21 # ------# This file contains definition for all actions | # (name) - [string] unique name of the action | # (cd) - [integer] cooldown of the action | # (cast_time) - [integer] time required to perform the action | # (range) - [integer] maximum range to find target of action | # (targetCount) - [string] targets: SINGLE/MULTIPLE/NONE | # (affilation) - [string] who to target: ENEMY/FRIENDLY/NA | # | # (name);(cast_time)(cd);(range);(targetting);(affilation) | # ------heal;1500;750;150;SINGLE;FRIENDLY fireball;20000;0;80;MULTIPLE;ENEMY spawn_guardian;20000;0;0;NONE;NA
Figure 2.6: Example of file action.tas enemy units in radius of 80 points. The last game action has name spawn guardian. Its cooldown time is once again 20000 milliseconds and casting time 0 milliseconds. This game action does not require a target, and therefore has zero range.
Similarly to game units, every game action needs to have its corresponding script. The filename has to be same as the game action name. We will further discuss how to write scripts for game actions in the section 2.14.
2.10 Scripting support
The file globals.lua serves as the main LUA module for scripting. As we previously mentioned, the file contains unique integer addresses for all game objects and sounds. More about this is discussed in the section 2.15. The module can be used in every script created and therefore can be particularly useful for implementing some functions which might be used by multiple scripts. In order to be recognized by our framework, scripts must obey the exact folder structure. All LUA scripts and modules are placed inside the folder scripts. This folder further contains three subfolders troops – where unit scripts are placed, towers – where tower scripts are placed and actions – where game action scripts are placed.
2.10.1 Debugging embedded scripts Embedded LUA scripts can be debugged. However, this feature is only available for platform Windows and the DEBUG directive needs to be defined. To start debugging we need to start Visual Studio Code, which have already installed the addon for debugging as described in the section 3.1. When the framework is running we can attach Visual Studio Code’s remote debugger to our process, which is a feature handled by the installed addon.
22 The remote debugger offers various commands to use when debugging. For the full list of supported commands, see the official page of the addon [31]. We will show the two most important commands. The first is !list which lists all available scripts – an index number and the name of the script on each line. The second is !switch index which sets the current debugged scripts based on the index numbers provided by the first command. Remote debugging supports common debugging tools, for example, break- points, conditional breakpoints, values of local variables and watches.
2.11 Scripting game entities
Our framework requires that scripts for game entities implement three func- tions. The functions have a single argument expecting an IDynamic instance:
OnInit(entity): This function is called only once, exactly when the game entity is about to enter the world. Therefore, this is an useful place to perform initialization of the game entity.
OnUpdate(entity): This function is called everytime the entity is updated by our framework. Due to this fact it is necessary to keep the update function simple, the use of extensive loops and performing unnecessary operations is highly unadvised.
OnDying(entity): This function is necessary for units only. It is called only once, exactly when our framework discovers the unit has died. After this func- tion, the unit is no longer updated.
Now we will discuss common scripting interface for all entities. These meth- ods are implemented by towers as well as units.
To provide support for storing entity’s state, game entities have overloaded operator [], called indexer. To check whether an entity has initialized a variable, we use method HasVariable which return true if the variable was initialized. Even though LUA does not require from programmers to initialize variables, our framework throws a ScriptingException to avoid errors when implementing scripts. DynValue this[int id] { get; set; } bool HasVariable(int id); Variables storage API for game entities Some mechanics may require time measuring. To support timers, our framework implements following methods. SetTimer creates a new timer, StartTimer starts the countdown, IsTimeUp checks whether the time is up and finally ResetTimer resets the timer. void SetTimer(int id, int milliseconds); void StartTimer(int id);
23 bool IsTimeUp(int id); void ResetTimer(int id); Variables storage API for game entities Registering delayed callbacks is also supported by our framework. Using the method AddEvent, it is possible to register an event to an entity’s schedule. When delayed time is over the callback is executed. void AddEvent(int milliseconds, DynValue function, IDynamic target) Delayed callbacks API for game entities For the sounds support, our framework implements following methods. PlaySound tries to play a sound and returns true, if the operation was successful. StopAll stops all sounds on a given level and does not preserve any state of the sounds. PauseAll pauses all sounds on a given level and preserves their state. ContinueAll continues all previously paused sounds on a given level. bool PlaySound(int id, int level); void StopAll(int level); void PauseAll(int level); void ContinueAll(int level); Delayed callbacks API for game entities To check game entity’s state, our framework implements following methods. CanExecute returns true if the game action with provided id can be executed. IsExecuting returns true if a game action is currently being executed. bool CanExecute(int actionId); bool IsExecuting(); Entity state checking API for game entities For executing game actions, we use the method ExecuteAction. void ExecuteAction(int actionId); Game action execution API for game entities To remove all previously targeted entities, we use the method RemoveTargets. void RemoveTargets(); Target removing API for game entities To access information about active upgrades, we use the method IsUpgrade- Active. bool IsUpgradeActive(int upgradeId); API for checking state of upgrades for game entities For visual effects support, our framework implements following methods. ApplyVisualEffectActionTarget and ApplyVisualEffectActionTargets apply the effect either on a single entity targeted by action or multiple entities. ApplyVisualEffectThis applies the visual effect on current entity. The effect then lasts during the given interval in milliseconds.
24 void ApplyVisualEffectActionTarget(int id, int duration ); void ApplyVisualEffectActionTargets(int id, int duration ); void ApplyVisualEffectThis(int id, int duration); Target removing API for game entities Additional displaying options for game entities are implemented using methods SetTransparency and RemoveTransparency affecting the transparency of the entity. void SetTransparency(float value); void RemoveTransparency(); Additional displaying API for game entities Specific scripting interfaces for units and towers together with examples can be seen in corresponding sections, for units in the section 2.12 and for towers in the section 2.13.
2.12 Scripting units
In this section we will describe API for implementing behaviors for units which we will then illustrate on examples.
To control the movement of units, we have two basic methods. ContinueWalk- ing, in case of an enemy unit, makes the unit to follow its path and for friendly units it makes the unit to walk to the spawnpoint. StopWalking makes the unit to stop moving and stays at the current position. void ContinueWalking(); void StopWalking(); Movement API for units To check unit’s state we can use four methods. IsInCombat return true is the unit is in combat with a different unit. CanCombatStrike return true if the unit can strike its combat target. IsPlayers return true if the unit is friendly. bool IsInCombat(); bool CanCombatStrike(int id); bool IsPlayers(); API for checking unit’s state Targetting other units is handled using a few methods. Target method tries to find a single unit based on supplied parameters. TargetAll method is used to target all units in a given range. All targeting methods return true, if at least one suitable target exists. bool Target(int actionId, TargettingModifier modifier); bool TargetAll(int actionId, int maxTargets = int.MaxValue); Targeting API for units
25 To handle unit’s combat, we can use two basic methods. InitiateCombat sets targeted entity as combat target. If the new combat target was not in combat, its combat target is set too. CombatStrike then attacks the combat target. void InitiateCombat(int id); void CombatStrike(int id); Combat API for units Additional unit displaying options are provided by some advanced meth- ods. SpeedUpAnimationSpeed, SlowDownAnimationSpeed and SetDefaultAnima- tionSpeed provide an option to alter the frame rate of animations for the unity. void SpeedUpAnimationSpeed(float multiplier); void SlowDownAnimationSpeed(float multiplier); void SetDefaultAnimationSpeed(); Advanced displaying API for units Additional method for units regarding visual effects support – ApplyVisual- EffectCombatTarget applies visual effect on its combat target. void ApplyVisualEffectCombatTarget(int id, int duration ); Visual effects API for units In the example below, we can see the implementation of an unit. This example can serve as a basic enemy unit’s behavior. local _table = require’globals.lua’; local variables = _table.variables; local actions = _table.actions; local sounds = _table.sounds; function OnInit(entity) end; function OnUpdate(entity) if (entity.IsInCombat()) then entity.StopWalking(); if entity.CanCombatStrike(actions.attack_slow) then entity.PlaySound(sounds.sword_slash, 1); entity.CombatStrike(actions.attack_slow); end; return; end; entity.ContinueWalking(); end; function OnDying(entity) entity.PlaySound(sounds.dying_enemy, 1); end; Example of an enemy unit behavior implementation
26 In the presented example, we can see a slightly modified implementation of an enemy unit called knight from the example game. Its update function consists of checking if the unit is in combat – if yes, it tries to strike its combat target. If the unit is not in combat they follow the path. When the unit dies it plays a sound signalizing its death. local _table = require’globals.lua’; local variables = _table.variables; local actions = _table.actions; local sounds = _table.sounds; function OnInit(entity) end; function OnUpdate(entity) if (entity.IsInCombat()) then if entity.CanCombatStrike(actions.attack_fast) then entity.PlaySound(sounds.axe_slash, 1); entity.CombatStrike(actions.attack_fast); end; return; end; obj.ContinueWalking(); if (entity.CanExecute(actions.eagle_eye)) then if (entity.Target(actions.eagle_eye, targettingModifier.Closest, false)) then entity.InitiateCombat(actions.attack_fast); return; end; end; end; function OnDying(entity) entity.PlaySound(sounds.dying_friendly, 1); end; Example of a friendly unit behavior implementation In the second presented example we can see the behavior of a friendly unit called axeman from the example game. Whenever in combat, this unit tries to strike its combat target with the game action called attack fast. Its method ContinueWalking is overriden to go to the spawnpoint but, in case of combat, it walks towards the combat target. The friendly unit tries to find the closest target for combat using the game action called eagle eye. After a suitable combat target is found it initiates the combat with the game action called attack fast. If this unit dies it plays dying friendly sound.
27 2.13 Scripting towers
Behaviors for towers are implemented in a similar way, compared to units. Once again, we will first describe available API for scripting and then present some examples. Targeting support is a bit different compared to units. Towers have different methods to target units for basic shooting – Target and TargetAll. Methods for targeting regarding game actions – TargetAction and TargetActionAll. All targeting methods return true, if at least one suitable target exists. bool Target(TargettingModifier modifier = TargettingModifier.NA); bool TargetAll(int maxTargets = int.MaxValue); bool TargetAction(int actionId, TargettingModifier modifier = TargettingModifier.NA); bool TargetActionAll(int actionId, int maxTargets = int.MaxValue); Targeting API for towers To check tower’s state and support basic projectile shooting, our framework implements following methods. CanShoot return true if the tower is ready to shoot, based on defined cooldown. Shoot fires a projectile at a targeted unit. ShootMultiple shoots at all targeted units. bool CanShoot(); void Shoot(); void ShootMultiple(); Projectile shooting API for towers Friendly units support is handled using following methods. Method GetSpawnedTroopsCount returns count of the spawned units. SpawnFriendly then spawns units based on given type and count. void SpawnFriendly(int id, int count); int GetSpawnedTroopsCount(); Friendly units support for towers Additional methods to for towers regarding visual effects support are following. ApplyVisualEffectTarget and ApplyVisualEffectTargets apply the effect on a single unit targeted for shooting or multiple units. void ApplyVisualEffectTarget(int id, int duration); void ApplyVisualEffectTargets(int id, int duration); Additional API regarding visual effects for towers An example of a basic behavior for a tower only shooting projectiles at units in range can be seen in the example below: local _table = require’globals.lua’; function OnInit(entity) end;
28 function OnUpdate(entity) if (entity.CanShoot() and entity.Target(targettingModifier.Furthest)) then entity.Shoot(); end; end; Example of a simple tower behavior implementation In the presented example we can see the script of a tower called arrow tower 1 from the example game. This tower simply checks if it can shoot – checks if the ability is not on cooldown and if a suitable target exists. If all conditions are satisfied the tower shoots at the targeted unit. local _table = require’globals.lua’; local actions = _table.actions; local troops = _table.troops; maxSpawned = 2; function OnInit(entity) entity.ExecuteAction(actions.spawn_unit, troops.axeman); entity.ExecuteAction(actions.spawn_unit, troops.axeman); end; function OnUpdate(entity) if ((entity.GetSpawnedTroopsCount() < maxSpawned)) then if (entity.CanExecute(actions.spawn_unit)) then entity.ExecuteAction(actions.spawn_unit, troops.axeman); end; end; end; Example of a different tower behavior In the second presented example we can see the script of a tower called troop tower 1 from the example game. This tower makes use of friendly units game mechanic. The script defines that it can spawn the maximum number of two game entities. The OnInit function executes two times the spawn unit game action. Then during the OnUpdate call it checks if another unit can be spawned. If yes it spawns another, otherwise the tower does nothing.
29 2.14 Scripting game actions
All scripts for game actions have to implement a single method, which will be called by our framework whenever the game action is executed.
Execute(target): The function is expecting an instance of IDynamic as ar- gument. Therefore, all methods are called on this instance and game action itself does not need additional scripting interface. local _table = require’globals.lua’; local variables = _table.variables; local sounds = _table.sounds; local effects = _table.effects; dmgValue = 10; function Execute(target) target.ApplyVisualEffectThis(effects.fxFireball, 5000) ; target.PlaySound(sounds.fireball, 1); target[variables.HP] = target[variables.HP] - dmgValue ; end; Example of a simple game action In the presented example we can see the script of a game action called fireball from the example game. It applies a visual effect with name fxFireball for the duration of 5000 milliseconds on the targeted entity. Then the game action plays a sound effect with name fireball on level 1. The last thing the game action does, is that it subtracts 10 hitpoints from the target’s hitpoints. local _table = require’globals.lua’; local variables = _table.variables; local sounds = _table.sounds; duration = 5000; function effectFade(target) target[variables.frozen] = false; end; function Execute() target.PlaySound(sounds.freeze, 1); target[variables.frozen] = true; target.StopWalking(); target.AddEvent(duration, effectFade, target); end; Example of a different game action
30 In the second presented example for game actions we can see a slightly more advanced game action called freeze from the example game. It plays a sound with name freeze on level 1, sets target’s variable with identifier frozen to true, makes the target entity stop walking and registers a delayed callback. The task of the callback is to destruct the game action effect which means setting the variable with the identifier frozen to false.
2.15 Registering for scripting
When referencing variables, game objects, sounds and upgrades we need to have assigned for each of them their an unique integer identifier. Even though each of the mentioned objects is already assigned an unique name, we do not allow the use of string for such referencing. Such usage of string identifiers would create performance bottlenecks and possible pressure on heap. An example of file globals.lua can be seen in the example below: local globals = { variables = { Alive = 1,-- Boolean[Read/Write] HP = 2,-- Int32[Read/Write] MaxHP = 3,-- Int32[Read/Write] Speed = 4,-- Float[Read/Write] Size = 5,-- Float[Read/Write] Cost = 6,-- Int32[Read/Write] Worth = 7,-- Int32[Read/Write] Cooldown = 8,-- Int32[Read/Write] Damage = 9,-- Int32[Read/Write] Range = 10,-- Int32[Read/Write] }, actions = { fireball = 5006 }, troops = { monk = 6000, knight = 6001 }, towers = { arrow_tower_1 = 7004 }, sounds = { sword_slash = 8000, fireball = 8002 }, effects = { fxHeal = 9000, fxFireball = 9001 }, upgrades = { tough_skin = 10002 } } } Example of registering in globals.lua module In the presented example we can see string identifiers being assigned their unique integer identifiers. Displayed variables Alive all the way to Range are required variables by our framework. It means that they need to stay in the file, however, their integer identifiers may be changed if the user needs such change. During the initialization phase framework loads all addresses and from this time on it refers to all objects only using these addresses. If framework finds an error in the file – two files are assigned the same integer identifier itthrowsa ScriptingException.
31 2.16 Adding upgrades
Each upgrade in order to be recognized need to be first defined in the file upgrades.tas. An example of this file can be seen in the Figure 2.7.
# ------| # This file contains definition for all upgrades | # (name) - [string] unique identifier of the upgrade | # (desc) - [string] description of the upgrade | # (type) - [string] affects: UNITS/TOWERS/SPECIALPOWER | # (dep1) - [string] upgrade dependency 1 | # .... | # (depN) - [string] upgrade dependency N | # | # Template for definition: | # (name);(desc);(type);(dep1),...(depN) | # ------| tough_skin;Gain 20% HP increase;UNITS;NULL tougher_skin;Gain 30% HP increase;UNITS;tough_skin
Figure 2.7: Example of file action.tas
In the presented example we can see the definition of two upgrades. The first with name tough skin has description: Gain 20% HP increase. It affects units and has no dependencies. The second upgrade with name tougher skin has descrip- tion: Gain 30% HP increase. Also affects units and has the previously defined upgrade as a dependency. This means that it can not be activated without the previous one.
From within the scripting environment it is possible to check which upgrades are active. This can be used then to create more advanced behaviors.
2.17 Adding maps
Maps for our framework are created using a third party program called InkScape. In this section, we will describe the process of creating a new game map using the mentioned program.
1. Preparation: Create a new file with the design resolution same asthe rendering resolution of the framework. For the map creation use only tools described below. Delete all layers created and do not add new ones. Layers are not supported.
2. Add a path: Choose the Bezier tool and using this tool draw the path as a polygonal chain, starting from the map entrance all the way to the map’s exit. The path needs to consist of one of more connected line segments only. When the path is finished, we need to assign an unique name to it.To perform this, open the properties of the path and fill the textbox labeled as
32 ID with the path’s name. We will need these identifiers later for the level definitions.
3. Add a towerpoint: Choose the Rectangle tool and set the color to yellow (#FFFF00), which is the default for yellow in InkScape. Now draw a rectangle somewhere on the map – this represents a place where towers can be built. Do not worry about its size because only the information about its position will be extracted.
4. Add a spawnpoint: Choose the Rectangle tool and set the color to red (#FF0000), which is the default for red in InkScape. Now draw a rectangle somewhere on the map. This represents the area to use when spawning friendly units. So when drawing a spawnpoint its area is important.
5. Assign spawnpoint to a towerpoint: Choose the Connector tool. Find the tower and the spawnpoint that need to be assigned. Join them using the selected tool.
Steps 2,3,4 and 5 can be repeated multiple times. An example of a map created in InkScape can be seen in the Figure 2.8. The only constraint is that each towerpoint must have exactly one assigned spawnpoint. When we are finished we save the map as a SVG file. For better clarity this process can be split into multiple SVG files, as drawing many paths into a single file can become unmaintainable. The last thing to do is to extract the information from these files into a better format for processing. This is achieved using the utility called MapDataExtractor.exe. Map data information can be generated using the following command: MapDataExtractor.exe map1.svg map1_pt2.svg Command to extract map data The utility expects all files regarding the same map as arguments from command line. The output of the utility is a single file with the extracted map data. The filename of the output has same prefix as the first argument andis concatenated with navi suffix and .tas extension.
33 Figure 2.8: Example of a created map using program InkScape. The yellow rectangles represent the points where towers can be placed. The red rectangles represent the area where friendly units can be spawned. The connected line segments represent paths.
Finished maps are then placed inside the maps folder of the framework. The SVG files are not needed by our framework anymore, it is sufficient to place thereonly files containing extracted map data information.
2.18 Adding levels
All levels in order to be recognized by our framework need to be first defined in the file levels.tas – each level on a single line. An example of this file can be seen in the Figure 2.9. # ------| # This file contains definitions for levels | # (level_name) - [string] unique name of the level | # (starting_resources) - [integer] starting resources count | # (hitpoints) - [integer] hitpoints for this current level | # (map) - [string] unique name of a map | # | # Example: (level_name);(starting_resources);(hitpoints)(map)| # ------| level1;200;3;map1
Figure 2.9: Example of file levels.tas
34 In the presented example we can see the definition of a level with name level1. The players start the level with 200 resources and have 3 hitpoints for this level.
Level descriptions provide all necessary information for our framework about the current level. This file contains information about the data to load, game events to register and user interface setup. An example of this file can be seen in the Figure 2.10.
# ------| # Template of a level definition: | # filename is texture to load as a background for the level | # BACKGROUND;(filename) | # filename is texture to load as an empty tower point | # FREEPOINT;(filename) | # troop_1 to troop_N are units to load for the current level | # TROOPS;(troop_1);...(troop_N) | # tower_1 to tower_M are towers to load for the current level | # TOWERS;(tower_1);...(tower_M) | # towerIcon_1 to towerIcon_L are icons of tower to load; | # these icons will be loaded into user interface in this order| # ICONS;(towerIcon_1);...(towerIcon_L) | # specialPower_1 to specialPower_J are special power icons | # to load; in this specific order together with corresponding | # game actions gameAction_1 to gameAction_J | # SPECIAL;(SP_1),(gameAction_1);...(SP_J),(gameAction_J) | # | # Game events section define what happens in this level | # (GameEvent_1) | # ... | # (GameEvent_K) | # ------| BACKGROUND;map1 FREEPOINT;action_point TROOPS;monk;knight;axeman TOWERS;troop_1;tower_2 ICONS;troop_1 SPECIAL;specialPower1,fireball
00:00:1.0000000;ANNOUNCEMENT;Welcome adventurer! 00:00:12.0000000;SPAWN;knight;path_0
Figure 2.10: Example of a level definition – file level1.tas
In the presented example, we created the definition of the level with name level1. As a background for current level the texture with filename map1 will be loaded. The texture to display on an empty tower place is called action point. Following units will be loaded: monk, knight, axeman together with following towers: tower 1 and tower 2.
35 In addition, our framework needs to assign user interface buttons according to the definition. We loaded two towers but defined only one tower icon. This means that unless tower 2 is not an upgrade from tower 1, the player will not be able to build this tower. Special powers are defined as pairs. The first item is always its texture name, while the other item is the corresponding game action. In the example we can see that we want to load the texture with name specialPower1 and assign to the game action called fireball. The rest of the file is dedicated to the definition of game events. Intheex- ample above, we can see the definition of two events. The first event is of type ANNOUNCEMENT and after the game time passed 1 second, the message Wel- come adventurer! will be displayed. The second game event is of type SPAWN and its task is to spawn an unit with name knight on a path with name path 0, which will happen after the game time passed 12 seconds.
Each level descriptor needs to be added to the project inside the folder named levels. After all the described steps are completed, the level is available to use in our framework.
2.19 Adding sounds
Adding sounds to framework requires the same process as described in the section 2.4. However, sounds must be listed in the file sounds.tas in order to be available for the scripting environment. An example of the file can be seen in the Figure 2.11.
#------| # This file contains filenames for all sounds | # ------| axe_slash sword_slash fireball freeze
Figure 2.11: Example of file sounds.tas
2.20 Adding custom fonts
Fonts are added into framework using the Pipeline tool. To correctly add a font we need to add .spritefont file and, in case of a non-standard font, .ttf file with the actual font. Example of a spritefont file:
36
As we intend to support various Android devices, we need to load more version of the same font. The framework automatically chooses the font based on the device’s DPI. However, for the framework to locate the fonts, we need to create them as follows: Four spritefont files have to be created. The fonts will have following names: font-ldpi, font-mdpi, font-hdpi and font-xhdpi. Where -ldpi are devices with low DPI and -xhdpi are devices with extra high DPI.
2.21 Adding user interface screens
To create a new user interface screen, we need to add a new XAML Window to the views. It is possible to create it from a template screen presented in the section 3.8. In this section we will try to show some examples on how to imple- ment basic user interface screens.
Using of absolute values when defining sizes and locations of user interface components is discouraged. Not following this advice will result in an user interface which can not be scaled. Instead containers and relative sizes should be used.
37 Example of using containers and margins to create scalable user interface. The grid splits its content into two rows where the height of the first row is two times bigger than second’s Displaying values from properties defined in view-model is achieved using binding. An example of binding can be seen in the example below:
38 Example of a resource dictionary We can save the example above as Dictionary.xaml and then add it to a user interface screen exactly as seen in the example below:
2.21.1 Visual Studio designer tool User interface screens can be also designed using the Designer tool in Visual Studio. It provides an interesting alternative for users not familiar with XAML – the designer tool generates the actual design code. More advanced modifications may however require also modification ofthe view-model and its handler as described in the section 3.9.
2.22 Example game
This section is dedicated to the description of the example game created using our framework. Information about licenses can be found in the attachment D. We are going to provide more specific information about what was implemented in the example game.
A loaded level in our example game can be seen in the Figure 2.12. The Figure was created on an Android device with a different resolution compared to the rendering resolution. As we can see the framework adjusted itself – pillarboxing the viewport as described in the section 3.7.7. In the top left corner of the player’s viewport we can see the selection of towers which can be placed on empty tower places. The fourth option from the left is the option to upgrade an existing tower.
39 Figure 2.12: Screenshot from the example game. We can see the beginning of the level1. Raiding enemy units are trying to reach the end of paths at the bottom of the screen.
In the bottom left corner we can see a textbox displaying game messages. There are two additional textboxes: one displaying how many lives has the player left and the seconds how many coins can be used to build or upgrade towers. We can also see the button labeled Skip which can be used to skip to the next wave of enemies. In to top right corner we can see the selection of special powers which can be used on enemy units. Moreover we can see a fighting friendly unit with an enemy unit. Onthe enemy unit we can see the visual effect of the heal spell which was recently casted by one of the monks to the right of the fighting units.
Units: four different units were implemented. knight – enemy unit which walks its path and whenever under an attack it retaliates. monk – enemy unit which walks its path and tries to heal the most wounded in its range. axeman – friendly unit spawned by troop towers, guards its spawnpoint and whenever an enemy gets into its range it starts combat. boss knigh – enemy unit with signifi- cantly greater hitpoints value than other units. Its behavior is same compared to knight. Unlike other enemy units, this one is resistant to the special power freeze.
Towers: three different tower types were implemented. troop tower 1 – tower capable of spawning friendly units. This tower has two possible upgrades, each further upgrade spawns more units. arrow tower 1 – tower capable of firing pro- jectiles at enemy units. This tower also has two possible upgrades, each of them makes the tower stronger. stronger tower – more powerful than other towers, firing at every enemy unit in its range. This tower does not offer any further upgrades.
40 Levels: two different levels are implemented, each of them played on adiffer- ent map. level1 showcases most of the mechanics implemented both in framework and the example game. It spawns some waves and at the end a final boss. level2 is a very simple level spawning a few monks at once.
Game actions: To implement already mentioned mechanics multiple game actions were implemented. fireball and freeze which are used as special powers were already described in the tutorials regarding scripting. heal can restore some of target’s hitpoints – this game action is used by monks. spawn guardian spawns a friendly unit – this is used by some towers. eagle eye is a simple game action used by friendly units to target enemy units. Game actions prefixed with attack are used for combat behavior.
Visual effects: For the example game two visual effects were created. fx- Heal is a visual effect which is displayed on target game entity of the spell heal. fxFireball is displayed on all targeted entities by the special power fireball.
Upgrades: Four upgrades were implemented for the example game. Upgrade tough skin grants player’s units 20% additional hitpoints. tougher skin grants them 30% more hitpoints compared to their basic value. super strength makes friendly units deal more damage. quickness makes friendly units move faster on the game map.
In the next subsection we are going to describe user interface in the example game. The user interface is fully scalable and therefore should look good on every device which provides correct information about its screen.
2.22.1 User interface in the example game The example game contains definition for several game screens needed forTD development. Each user interface screen is created to be scalable.
MainMenu.xaml: This user interface screen contains five buttons labeled: Play, Upgrades, Credits, Replays and Exit. Each of the buttons, except for Exit takes the player to a different user interface screen. Exit saves and quits the game.
PlayMenu.xaml: In this user interface screen we implemented the level se- lection mechanism. It is implemented using nine buttons serving as a level page, together with buttons labeled Next and Previous switching either to the next or previous level page. When a level is selected, the player can press the button Play which loads and starts the selected level.
ReplayMenu.xaml: Available replays are displayed as a list where one of them can be highlighted by the player. After the player presses a button labeled Start framework starts the selected replay.
Credits.xaml: This is a very simple user interface screen only displaying credits and information about the game.
41 Upgrades.xaml: Upgrades is an user interface screen where user can ac- tivate or deactivate upgrades. It consists of three tabs – unit upgrades, tower upgrades and special power upgrades. Whenever the player chooses an upgrade its information is displayed in provided textboxes. Immediately after leaving this user interface screen the state of active upgrades is saved for future games.
HUD.xaml: This user interface screen is implemented as transparent and is displayed as an overlay over the actual game. It provides three image buttons for towers, one image button for upgrade option and two image buttons for special powers. In addition the screen also contains a textbox, where in-game messages are displayed. This screen also has a regular button labeled Skip – this is visible only for levels supporting the feature of skipping the a next wave. This screen also defines simple pause menu consisting of three buttons labeled: Continue, Save replay and Back to Main menu.
42 3. Development documentation
The third chapter is dedicated to the development documentation of our framework. We will discuss its implementation in C# programming language. We start by describing the solution with the most important design decisions which had an impact on further implementation. In the rest of the development documentation we describe the key components of our framework.
3.1 Development tools
Before we get to the information about the solution we need to mention the tools that were used during the development. Some of the tools are required and some are optional – it depends on which features of the framework we want to use.
• Visual Studio 2015 Community Edition
• .NET framework 4.5
• MonoGame 3.5
• optional (LUA debugging): Visual Studio Code 1.7.2 with mentioned MoonSharp extension
• optional (Android development): Xamarin 4.3.0.784
All other required libraries were added to the solution as NuGet packages [33] and for the development of framework we used following builds:
• MoonSharp v2.0
• EmptyKeys v3.0
3.2 Top-down solution design
In this section we are going to describe the solution. We start by presenting its structure with the description for each project. Then we continue by describing time steps in MonoGame library – this is a very important concept which will help us to better understand how the library and also our framework work.
3.2.1 Solution structure The solution for our framework consists of following projects:
GameUILibrary.csproj: Portable class library UserInterface.csproj: WPF user control library TowersAndSwords.csproj: MonoGame Windows project TowersAndSwordsAndroid.csproj: MonoGame Android project
43 According to the documentation on MonoGame’s website [6], it is not possible to build from a single project for both Windows and Android. Due to this limitation, the main project has two versions under the same solution – one for each platform. However, the source code remains same because any platform specific code is resolved using preprocessor directives. Information about where to find the solution is described in the attachment A.
3.2.2 Model-view-viewmodel pattern The solution is implemented using the MVVM design pattern, where the model is TowersAndSwords.csproj project and TowersAndSwordsAndroid.csproj – which is the same project each for different platform. The viewmodel is GameUILibrary.csproj and the view is UserInterface.csproj.
UserInterface.csproj: This project consists of XAML files. The files contain definitions of screens which are built using user interface components and other constructs supported by EmptyKeys. The screens are fully declarative. Components may bind some of its properties to the public properties provided by view-model. GameUILibrary.csproj: This project as a view-model provides an abstraction of the view. The main task for view-model is to prepare data in the right format to be displayed by view. However view-model has no information about the actual view layout or structure as well as no information about the actual data format used by model. TowersAndSwords.csproj, TowersAndSwordsAndroid.csproj: Main projects own all the data and implement framework’s logic. The logic will be further described in the following sections.
3.3 Overview of the solution architecture
In this section we present an overview of the solution architecture which can be seen in the Figure 3.1. This overview shows the most important components which will be further described in the following sections.
44 ViewModel: GameUILibrary.csproj View: UserInterface.csproj
View model UI screens
View model handler
Model: TowersAndSwords.csproj and TowersAndSwordsAndroid.csproj
Game events
Level state World
Game objects
Sound system User interface handling
Data storage Framework Spritesheet packing
Persistent storage Input/Output Entrypoint
Figure 3.1: Simplified object diagram of the solution. The diagram presents visualization of the key components and their relations across the solution.
3.4 Game real-time progression
We already mentioned that TD games are real-time strategies. Due to this fact we need to create an illusion of continuous time progression. Even though the game loop is already implemented by MonoGame, it is important to understand the timestep used by this library.
3.4.1 Timestep modes in MonoGame MonoGame is responsible for periodically calling certain methods on our framework, these methods are then responsible for updating and drawing the game. However, the time period between two calls of a method may vary:
Fixed timestep: This is the best effort approach which is also the default behavior. Using the fixed timestep approach, MonoGame calls periodically update and draw method with the same time period.
45 Variable timestep: Whenever MonoGame detects that the game is running too slow, update calls become prioritized in order to catch up. However, this negatively affects game’s frame rate. If even prioritizing is not enough to catch up, the game fails to run correctly. If the game fails to run correctly, due to target’s hardware not being fast enough, in Framework’s constructor we can set property TargetEllapsedTime to a different value. The default value used by MonoGame and also our framework is equal to 1/60. This represents the period between two update calls. The default setting means that update will be called sixty times a second. Making the period bigger reduces the stress made on hardware and may be useful for slower devices.
3.4.2 Game loop in MonoGame After describing the time step modes used by MonoGame we can now introduce the game loop pattern. Its purpose is to split the logic of our framework into five parts which we will further described in this section. Visualization of the game loop pattern can be seen in the Figure 3.2.
Startup part
Update part Wait
No Terminate Draw part
Yes
Cleanup part
Figure 3.2: Flowchart visualizing game loop pattern
Startup part: This part runs before the control flow actually enters the game loop. Startup is called only once and therefore is particularly useful to, e.g., perform components initialization or content loading. Update part: During the game loop this method is called multiple times each second. However, as described in the section 3.4.1 the actual period between two calls may vary. This part is useful to update all game objects and perform other necessary actions to ensure the game is running correctly. Draw part: Similarly to the update part, the draw method is also called multiple times each second. This part must be used only for drawing of the active game objects. It is not suitable for updating of any kind as it is not guaranteed to be called with the same time period. This is due to the variable time step described in the section 3.4.1.
46 Clean-up part: After the game loop terminates, the clean-up part is respon- sible for disposing of all unmanaged content. This method is called only once, right before the program exits. Regarding the content loaded using MonoGame’s content manager, the content is unloaded automatically by MonoGame. However, not disposing of any other unmanaged content results in resource and memory leaks.
MonoGame has already implemented the game loop pattern. The main class of our solution Framework extends MonoGame’s class Game. Framework than overrides its methods Initialize – startup part, Update – update part, Draw – draw part, UnloadContent – clean-up part.
3.5 Framework initialization phase
In this section we will describe the process of creating an instance of our framework and its startup process.
The entrypoint is in the class Program for the Windows project and in the class Activity for the Android project. Their task is to create an instance of Framework and run it. In addition to this, Activity overrides methods OnPause and OnResume. These methods resolve situations when our framework loses focus – this happens whenever another application becomes active. Immediately after our framework is constructed, its method Initialize is called, which means the framework is entering the startup part. During startup part we create and initialize all necessary objects to ensure correct runtime. At the end of the Initialize method we call LoadContent method. At this point the LoadContent needs to load addresses of all game objects from globals.lua together with information about available game objects and lev- els. The main scripting module globals.lua is described in the section 2.15. Sound effects and graphics needed for user interface are loaded too.
Our framework can be in one of the following internal states: Startup, In- Menu, LoadingReplayMenu, CreatingNewGame, CreatingNewReplay, Game, Re- play, EndGame and Paused. Most of them are changed as a result of player’s interaction with user interface – user interface screen transitions. Other transi- tions, for example, from CreatingNewGame to Game happens right after all data for the current level is prepared.
After the Initialize method successfully finished its job, our framework enters the game loop and displays main menu. Until a level is started, its job is really simple. Framework updates and draws GameUIHandler which is sufficient to keep the main menu working. Our framework remains in this state until user decides to start a level. Flowchart of this can be seen in Figure 3.3.
47 Windows: Program.Main Android: Activity.OnCreate
Framework.Initialize
Framework.initializeScripting
Framework.initializeUserInterface
Framework.LoadContent
Framework.Update Framework.Draw
Wait
GameUIHandler.Update GameUIHandler.Draw
Figure 3.3: Flowchart visualizing the initialization process of our framework and entering the game loop.
3.6 Framework gameplay phase
As described in the section 3.4.2, MonoGame tries to periodically call Update and Draw methods on our main class Framework. Those game objects which periodically need to be updated and redrawn (units, towers, projectiles and visual effects) are created in a similar manner, having their own update and draw methods. This results in delegating most of the job to the specific objects. Calling Update results in our framework to call update on current World, PointingDevice, Camera, and GameUIHandler instances, as seen in Figure 3.4. These classes are further responsible for updating all its content, especially World instance for calling update on all active game objects. Calling Draw results in a similar action. Most of the job is delegated to the World, which calls draw method on all active game objects. As the last step of this call, we draw the user interface as an overlay over the game. To achieve this, we call draw on GameUIHandler.
48 Framework.LoadContent
Framework.createNewGame
Framework.Update Framework.Draw
World.Update World.Draw
PointingDevice.Update
Camera.Update
Wait
GameUIHandler.Update GameUIHandler.Draw
Figure 3.4: Flowchart visualizing the flow of the main project after a level has been loaded. MonoGame periodically calls its update and draw methods.
3.7 The main framework projects
In this sections we present a more detailed development documentation for both MonoGame projects. We start this by listing all its namespaces with a short description for each of them:
TowersAndSwords: The main namespace contains the main framework class with other basic classes and interfaces that were not specific enough to be placed in a different namespace.
TowersAndSwords.InputOutput: All classes which are responsible for handling reading and writing game files are centered in this namespace. An integral part of this namespace provides support for pointing devices.
TowersAndSwords.Content: This namespace provides classes for storing game data together with sounds support, some game objects and sprite sheet packing capabilities.
49 TowersAndSwords.Entity: This namespace contains definitions and im- plementations for towers and units.
TowersAndSwords.Level: All classes containing support regarding levels and game events are centered in this namespace.
TowersAndSwords.Scripting: This namespace provides implementation of a scripting engine which unifies some methods used for implementing scripting interfaces.
TowersAndSwords.Diagnostics: This namespace is used for diagnostics purposes. The namespace contains custom exception definitions and a logger class used to log unexpected events.
TowersAndSwords.Technical: This namespace provides some classes nec- essary to ensure correct framework runtime, for example, timers, camera and object pooler. The classes are not directly related to the game itself or the TD genre.
Later in the following sections we will further discuss the namespaces together with their implementation.
3.7.1 Game objects component Game objects are defined by the abstract base-class GameObject in the de- fault TowersAndSwords namespace. Specific game objects are then implemented across two different namespaces and their subnamespaces:
TowersAndSwords.Entity: This namespace provides the definition and implementation for towers and units. The class diagram of this namespace and its subnamespaces can be seen in the Figure 3.5. Entity which implements IEntity interface serves as the abstract base-class for all entities. The class provides the basic support for scripting, introducing the option to store variables, operating timers and creating game events from the scripting environment. For the scripting purposes, instances of Tower, EnemyTroop or FriendlyTroop are exposed only through scripting interfaces provided by these entities. This constraint ensures that it is possible to call only those methods which were des- ignated to be called from the scripting environment. Any attempt to call or reference something not defined by the scripting interfaces or the LUA itself will result in an exception.
TowersAndSwords.Entity.DynamicEntity: This namespace provides Dynamic which is the abstract base-class for all units. More specific implementation of units is then in the abstract class Troop, this class implements the scripting interface IDynamic. Later on, we will use this interface to implement behaviors of units. The class Dynamic provides both the update and the draw method for units. The class is responsible to run unit’s script as well as process its scheduled game
50 IGameObject
GameObject Abstract Class
IEntity ILocalizable
GameEntity Abstract Class GameObject
IBoundable IBoundable IDrawable IDrawable
Dynamic Static Abstract Class Abstract Class GameEntity GameEntity
IDynamic IStatic
Troop Tower Abstract Class Class Dynamic Static
EnemyTroop FriendlyTroop Class Class Troop Troop
Figure 3.5: Class diagram of game entities events. The class Troop then extends Dynamic, implementing the IDynamic interface to provide advanced scripting options. EnemyTroop class further extends Troop – providing implementation for enemy units. Unlike friendly units, enemy units follow a path and attack only when being under an attack. FriendlyTroop class further extends Troop – providing implementation for friendly units. Unlike enemy units, friendly units guard their spawnpoint and are capable of initiating a combat with an enemy unit.
TowersAndSwords.Entity.StaticEntity: This namespace provides Static, which is the abstract base-class for all towers. Tower class which implements IStatic interface then contains the actual implementation for all towers. The namespace also contains additional game objects in order to support towers and friendly units placement on maps, namely the classes ActionPoint and SpawnPoint. Similarly to Dynamic, Static class implements both the update and draw method and is responsible for the tower’s script execution and game events
51 processing. Tower then extends Static and implements IStatic interface to provide ad- vanced scripting options.
TowersAndSwords.Content: This namespace provides implementation of a few additional game objects. Their class diagram can be seen in the Figure 3.6.
IGameObject
GameObject Abstract Class
IVisualEffect IDrawable IDrawable IDrawable IGameAction
Level Path Projectile VisualEffect GameAction Class Class Class Class Class GameObject GameObject GameObject GameObject GameObject
Figure 3.6: Class diagram of some game objects
Level is a representation of a level with basic information. Most data have to be loaded prior to starting the level. Path provides a basic implementation for working with path systems in our framework. The path data is loaded also on demand before the level starts. Projectile provides a basic implementation for projectiles that can be fired by towers. Instances of this class can be fired at a unit, having different parameters like speed, damage and cooldown. VisualEffect implementing IVisualEffect interface provides basic functionality for visual effects. Visual effects can be placed upon an entity from the scripting environment. Instances of the class handle the animation and change the position to draw according to the supplied entity. GameAction is an implementation of the interactions between game entities or between the player and game entities. Similarly to game entities, each game action has an associated LUA script.
3.7.2 Input/Output component The support for input and output is entirely implemented inside the subname- space InputOutput and its subnamespaces. Together it provides all classes nec- essary to support operations with game files and pointing device – mouse for Windows platform and touch screen on Android. This namespace is further de- composed into the following parts:
TowersAndSwords.InputOutput: GameDataLoader is the main class re- sponsible for loading all framework’s configuration files – basically every file with an extension .tas, except for those in subsolders maps and levels. Those files are loaded prior to starting a level or a replay. It implements the IGameDataLoader interface. Other classes can be used to write replays – using ReplayWriter or
52 store overall game progress – using GameProgressWriter.
TowersAndSwords.InputOutput.Parsers: Parsers are used to correctly parse and validate game data files. Whenever a game file can not be further parsed, an exception is thrown, then using the Logger class we record the corrupted file and corresponding line where the parser failed. Our framework has to work with three kinds of files. First are those that define game information and these files are parsed during the game initialization phase. Second files are those that need to be parsed and loaded prior to starting a level, containing information about the current level. The last category of files are those which contain either game progress or replays. For each of the mentioned category we have a parser, as seen in the Figure 3.7. GameInfoParser, LevelDataParser and ReplayDataParser are the main parsers, responsible to parse the game files. In order for them to work correctly, they have to be supplied with parsers implementing the same base-class for all the record types found in the corresponding game file. The main parsers then parse the game file using supplied parsers, and whenever they read a new record type, they find the appropriate parser from the supplied ones to process the next line. If such parser does not exist we consider the game file to be corrupted and an exception is thrown.
IParser IParser IParser IGameInfoParser ILevelParser IReplayParser
InfoParser LevelParser ReplayParser Abstract Class Abstract Class Abstract Class
GameInfoParser LevelDataParser ReplayDataParser Class Class Class InfoParser LevelParser ReplayParser
Figure 3.7: Class diagram of parsers
TowersAndSwords.InputOutput.PointingDevices: As we are targeting platform Windows and Android, we need to support different pointing devices regarding the user input. For platform Windows, we present the class WindowsPointingDevice which provides support for mouse input. For platform Android, we present the class AndroidPointingDevice which provides support for touch screens. Both classes implement the same base-class PointingDevice and IPointingDevice interface, as seen in Figure 3.8.
53 PointingDevice Abstract Class
IPointingDevice IPointingDevice
WindowsPointingDevice AndroidPointingDevice Class Class PointingDevice PointingDevice
Figure 3.8: Class diagram of pointing devices
3.7.3 Data and Persistent storage component The support for data storage is implemented in the Content.Storage subname- space. The class Database, which implements the IStorage interface, provides API for game data storage. To gain better performance when referencing objects, each of the game object is assigned an unique integer identifier. These identifiers are defined in the main scripting module globals.lua as described in the section 2.15 and loaded during the game startup part. Database stores each game object as a template created in the initialization phase. Templates are basically preprepared objects having basic information about them loaded, however, not having assigned data like graphics or scripts. Later, whenever the framework needs to create a certain object, Database can perform look-up based on either its name or identifier to return its template. Game object have implemented copy constructors which enables their duplication. The use of these templates minimize the need to access the physical storage and load the definitions over and over again. GameProgress holds information about the global game state. This means that it stores information about all passed levels, active upgrades and available upgrade points. This data is serialized on game exit, as it is meant to be persistent.
3.7.4 Sound system component The support for sounds is implemented in the Content.Audio subnamespace. The class SoundEffectSystem, which implements the ISoundSystem interface provides the sound playback API for our framework. The class basically tracks the information of all playing and stopped sounds. The information about sounds is stored in the instances of the SoundEffectState class. Implemented sound system solves two problems. The first problem is performance, which was solved by creating a pooler which allowed us to reuse old sound instances. The second problem is stuttering – this occurs when multiple instances of the same sounds are played simultaneously. This problem was solved with a constant MIN DELAY SAME SOUNDS in the static class Parameters. It defines the minimal period in milliseconds between two instances ofthesame sound can be played.
54 SoundSystem offers method TryPlay which returns boolean value based on whether the sound could be played or not. We have to mention the fact that the playback of a sound can be denied by the given device itself, as various platforms and devices may be supporting different number of sounds playing at the same time.
3.7.5 Spritesheet packing component Spritesheet packing capabilities are implemented inside the subnamespace Content.SpriteSheetPacking. The class SpriteAtlas, which implements the ISpriteSheetPacker interface provides a mechanism to create spritesheets out of smaller textures. The goal of the process is to minimize the total number of spritesheets. The use of spritesheets ensure that the graphics card does not need to switch textures too often and thus gaining better performance when drawing. The described combinatorial problem is known as 2D bin packing problem [34], which is NP-hard. Due to this fact, we implemented the following heuristic:
1. Sort all textures by their size.
2. We are going to build a binary tree, where leaves represent empty space.
3. In every iteration we try to find a leaf big enough for the texture (firstfit approach) – after we add the texture we partition the remaining free space. This creates two child nodes. The left child is assigned the free space under the texture and the right one is assigned the free space to the right of the texture.
4. Whenever we are not able to add a texture, we create a new spritesheet and continue.
In addition, we needed to track duplicities when two objects share the same texture. The result of spritesheet packing process is returned as an instance of the class Spritesheet from subnamespace Content. These instances track information about game objects which have their graphics content inside. Whenever an game object requires to access the spritesheet it gets exact information about the rectangle which needs to be drawn from the spritesheet. Whenever an animation needs to be drawn the game object is returned a list of rectangles, where each rectangle represents one frame of the animation.
3.7.6 Level state, Game events and World component The subnamespace Level with its subnamespaces contains all classes necessary for supporting levels. The class World, which implements the IWorld interface represents current level and contains collections of all active game objects. World is then responsible for calling update and draw methods on all active game objects. Holding all game objects, World is responsible for performing queries whenever an unit or a tower is looking for a target to perform a game action on. World also holds a schedule of game events – each event has its own activation time. Whenever an event reaches its time, it is executed.
55 LevelState holds information about the current level state. One of the respon- sibilities is also to record the input from a player. If the player later decides to store the gameplay, the game events will be written to the physical storage.
TowersAndSwords.Level.GameEvents: This namespace contains imple- mentation of all game events. Basically, there are two kinds of game events in our framework: those loaded either from a level definition file or created during the runtime and those that represent player’s input. All game events extend the abstract base-class GameEvent, as can be seen in the Figure 3.9.
GameEvent Abstract Class
AllowJumpForwardEvent AnnouncementEvent DelegateActionEvent Class Class Class GameEvent GameEvent GameEvent
DelegateScriptEvent LabelEvent ReplayJumpForwardEvent SpawnEvent Class Class Class Class GameEvent GameEvent GameEvent GameEvent
ReplayBuildEvent ReplaySpecialPowerEvent ReplayUpgradeEvent Class Class Class GameEvent GameEvent GameEvent
Figure 3.9: Class diagram of game events
We will start with the description of the first category of game events – those that are loaded either from a level description file or created during runtime: • AnnouncementGameEvent is used to display an in-game message defined in the level definition file. This is useful to let players know about some special actions that are about to happen, for example, letting the player know that a strong wave of enemies is being prepared to attack. • SpawnEvent spawns an enemy unit based on the the level definition file. Each spawn event requires name of the unit to spawn and an identifier of a path where to send it. • AllowJumpForwardEvent signalizes the framework that the skipping to the next wave feature should be enabled. Whenever players then decide to skip, the game time is set to the time of occurrence of the next LabelEvent. The second game event disables the skipping to the next wave feature. • DelegateActionEvent and DelegateScriptEvent perform delayed callbacks. These events can be created either from the scripting environment or by the
56 framework. DelegateScriptEvent is a game event created from the scripting environment and registered for a specific game entity. The use of this game event can be seen in the section 2.14, specifically in the second example – implementation of the freeze’s fade effect using the method AddEvent. We continue by describing the second category of game events – those that represent player’s input. Based on the interactions the player is able to do during a game, the following game events are important for us: • ReplayBuildEvent means that player built a tower. As each other event it stores the information about time of occurrence but this event also stores information about the tower and the position where it was built.
• ReplayUpgradeEvent means that player upgraded an existing tower. Similarly to the build event we need to know exactly when and which tower was upgraded. • ReplaySpecialPowerEvent means that player used a special power. Apart from the time, to correctly replay this event we need to store information about the special power which was used and the location where it was executed. • ReplayJumpForwardEvent means that player decided to use the feature of jumping to the next wave of enemies. The time of this event is sufficient for us because the corresponding LabelEvent, where the player jumped to, must be loaded before the start of the replay. Due to the fact that our framework is deterministic, to replay a game we can simply load the level and instead of reading user input we execute the recorded replay events.
3.7.7 User interface and other framework components In this section we are going to describe classes necessary for ensuring correct framework runtime. Nevertheless, these classes are not directly related to the gameplay or TD genre. These classes are implemented in the Technical subnamespace. BoxingViewport is responsible for handling different resolutions and aspect ratios of device screens. This is particularly important to ensure correct presentation on Android devices. The result of BoxingViewport is simply upscaling or downscaling if the monitor aspect ratio is same as render target. When the aspect ratios do not match, letterboxing or pillarboxing effect is created – player sees black bars on the left and right side of the display for pillarboxing effect and on the top and bottom of the display for letterboxing. Camera2D is a simple implementation of a camera. Its responsibility is to provide a view matrix for the draw method. Basically, if the game requires more advanced camera, the camera can be extended and its methods Update and GetViewMatrix overriden. The new camera can be then setup in the Framework’s Initialize method. GameTimeHandler is a singleton class providing simple, yet necessary function of correcting the process of game time tracking. MonoGame itself
57 provides a very useful GameTime class, which measures elapsed time between two calls. However, the problem with the class is that whenever we pause the game it still continues to tick. This results in a following situation: after the game continues we ask the class how much time passed to update the game objects. GameTime returns whole time period since the last update which happened before the game was paused – this creates problems when timing certain actions. To eliminate this issue GameTimeHandler provides methods Pause and Continue which correct the GameTime timing according to our needs. GameUIHandler is mainly responsible for calling update and draw methods on user interface. Apart from that this class is also responsible for switching user interface screens. ObjectPooler is a collection for storing game objects which can be later reused. Some game objects are frequently allocated and deallocated because their life-time is really short. Such behavior makes garbage collector [35] run quite frequently. Also repetitive allocation results in performance loss. When using object pooler old objects are not cleared by garbage collector and can be reused multiple times. Timer is a class providing basic functionality to measure different time pe- riods. SpellTimer is extending Timer, adding further features to track casting times and cooldowns.
Even though not part of the Technical subnamespace, we need to mention ScriptingEngine from subnamespace Scripting. This is a singleton class designed to simplify implementation of the scripting interfaces. This class contains many useful methods for scripting.
3.7.8 Custom file format Framework uses files with format similar to CSV [36]. The difference isthat the values are not separated by comma but instead by semicolons. Basically, it is a very simple file format, expecting a certain sequence of values separated by semicolons. The last difference is these files can contain comments. Whenever a line starts with # character or contains only whitespace characters, it is ignored. All files which use this specific format have .tas extension. These files will be individually covered in the following sections:
• troops.tas in the section 2.7
• towers.tas and staticAssets.tas in the section 2.8
• actions.tas in the section 2.9
• levels.tas together with individual level definition files in the section 2.18
• upgrades.tas in the section 2.16
• sounds.tas in the section 2.19
• animations.tas in the section 2.5
• effects.tas in the section 2.6
58 • map files which represent output generated by the MapDataExtractor utility described in the section 3.10
3.7.9 Scripting environment As we already mentioned, behavior for all entities and game actions are in our framework scriptable in LUA. In this section, we are going to further describe how is scripting implemented using MoonSharp library.
CLR [37] integration: All classes which will be used inside the scripting environment have to be first registered using the class UserData from namespace MoonSharp.Interpreter.
Conversions: Almost everything in the scripting environment is represented as an instance of DynValue. This means that whenever an object is passed between CLR and scripting environment, a conversion has to be done. Even though MoonSharp implements many conversions, both CLR to script and script to CLR conversions, it is highly advisable to avoid them as they can get really slow. The recommended approach is to either write own custom con- verters and register them inside Script.GlobalOptions.CustomConverters, or use only the interface provided by DynVal.
Using scripts: By calling the DoString method on an instance of the class Script, we initiate the process of compiling the code into an intermediate language [38]. In addition, MoonSharp fills script’s table with global identifiers fromthe provided code. From this table we can extract functions which can be later called. A simple example can be seen in the example below: Script script = new Script(); // loading code, square will be added to Globals table script.DoString(@" function square(n) returnn*n end"); // retrieving the square function DynValue squareFunction = script.Globals.Get("square");
/* Calling the square function and passing parameter as DynValue. We are avoiding automatic conversions by using one of the factory methods on DynValue*/ DynValue result = script.Call(squareFunction, DynValue.NewNumber(2)); // retrieving the result ==4 int convertedResult = result.Number; Script example using MoonSharp library
59 3.8 UserInterface.csproj
This project contains user interface screens for our framework. Screens are written in XAML without code-behind support. Template for creating an empty screen can be seen in the example below:
An empty user interface screen According to the documentation on EmptyKeys website [25], we set post- build event of the project to generate partial classes out of the screens. The partial classes have to be then added to GameUILibrary.csproj inside its folder GeneratedUI to be recognized by our framework. They will physically appear in the appropriate folder, however, they need to be manually added to the project.
Examples on how to define advanced user interface screens will be discussed in tutorials section 2.21.
3.9 GameUILibrary.csproj
This project serves an abstraction of view, therefore its main responsibility is to prepare data in the right format to display by view.
The class ViewModel extends the abstract class ViewModelBase provided by EmptyKeys. ViewModelBase provides services mechanism, which makes it possible to initiate method calls inside other projects. ViewModelBase then
60 extends the class BindableBase, which provides mechanism for creating bindable properties by implementing the INotifyPropertyChanged interface. Users creating user interface screens for our framework should place all data required by views inside the ViewModel class as following bindable properties: private MyType myBindableProperty; public MyType MyBindableProperty { get { return myBindableProperty; } set { // this raises property changed event for view SetProperty(ref myBindableProperty, value); } } Example of a bindable property for view-model This approach works for displaying the data, however we need to handle more advanced user interface events, for example button clicks. This is resolved in a similar manner – for example, buttons bind their Command property to view- model’s ICommand which implements the handle as seen in the example below: // Button binds Command to ICommand property public ICommand MainMenuCommand { get; set; }
// We implement its handler MainMenuCommand = new RelayCommand(new Action
// Retrieving it inside View-model private IGameService gameService = ServiceManager.Instance.GetService
61 Even though view-model does not have access to the actual implementation we can still use it under the IGameService interface. As mentioned before this can be used to perform method calls inside other projects.
For model to be able to interact with view-model GameUILibrary provides the class ViewModelHandler, which implements the interface IViewModelHandler. This class implements methods required to control the content of the view-model. However, if the user requires more advanced user interface screens that already available for the example game, view-model and its handler might require some alterations as well. By more advanced we mean adding new user interface controls, for example, buttons. As we already described alterations of the view-model are generally straight-forward – all the data we need to display have to be implemented as bindable properties as previously described in this section. However, the use of properties results in the problem that some methods need have some parts hard-coded. Specifically the following methods:
• SetLevelDescriptions needs to be altered if the user decides to have different amount of levels displayed per page in the level loading menu
• ShowHUDButtons needs to be altered whenever the user adds a new button or removes an existing one in the user interface screen HUD.xaml
• HideHUDButtons same condition as for the showing method
• UnMarkAll needs to be altered whenever we added a button which can be highlighted in the user interface screen HUD.xaml, for example, new tower button or new special power button.
• SetTowerData needs to be altered whenever the count of the tower buttons changes
• SetSpecialPowerData needs to be altered whenever the count of the special power buttons changes
Even though this might not look very attractive at first, the methods are quite simple and each of them has highlighted the parts which needs to be modified. Moreover every added button can be implemented in the same way as the ones which are already implemented – the only change required is to supply new properties. To implement handles for the new buttons we can use the GameService. All modifications regarding design of the user interface screens do not require any changes in the view-model.
3.10 MapDataExtractor utility
This utility extracts map data: information about paths, places to build tow- ers and spawnpoints. Input consists of one or multiple SVG files – which we showed how to create in the section 2.17. The utility was implemented using C# language and compiled for platform Windows as a 32-bit executable console
62 application. Information about where to find the solution and the compiled exe- cutable is described in the attachment B.
Basically, this program loads SVG files passed as command-line arguments. The files are then searched for map data. After the last file is processed, extracted map data is written to a file. Format of the output file can be seen in the Figure 3.10. PATH;path_id_0;x0_0,y0_0;x0_1,y0_1;....;x0_n,y0_n . . PATH;path_id_n;xn_0,yn_0;xn_1,yn_1;....;xn_m,yn_m POINT;px_0,py_0;SPAWNPOINT;sx_0,sy_0,sw_0,sh_0 . . POINT;px_k,py_k;SPAWNPOINT;sx_k,sy_k,sw_k,sh_k
Figure 3.10: Output file format of MapDataExtractor utility
Each path is represented with leading token PATH, following with its unique string identifier and sequence of points. Places to build towers are represented with leading token POINT, following with their actual place on 2D plane as a point, then with token SPAWNPOINT and four values representing the area of the spawnpoint.
3.11 Expanding the project
We are going to dedicate this section to showing some places which can be easily extended. This can be then used to implement new features or alter the existing ones. Even though these are certainly not the only places awaiting pos- sible extensions, we believe that the options described below may be ideal places to implement some extensions:
Extending scripting interfaces: We already mentioned ScriptingEngine, which is the class meant to simplify implementation of scripting interfaces. Being implemented as a singleton, it is easy to access from multiple classes across the project, while still ensuring that only a single instance exists. Furthermore, ScriptingEngine always contains reference to the current in- stance of IWorld as well as IStorage, making it easy to interact with other game objects. After implementing a new feature, we can extend the scripting interface IDynamic or IStatic to support the new feature and thus making it available for the scripting environment.
Extending game service: The interface IGameService defines methods which view-model can call. Similarly to ScriptingEngine inside the implemen- tation class GameService it is easy to implement new methods which can be using the interface made available to the view-model. GameService also contains references to the current instance of IWorld and IStorage making it possible to
63 interact with most of the important framework components.
64 Conclusion
To conclude this thesis, let us summarize what we created an how it matches project goals set up in the introduction of the thesis. We designed and implemented a framework for development of TD games with following features:
• The process of adding game objects and content is simple. Adding content is supported by Pipeline tool and game objects are defined by modifying textual files.
• Game maps are created using a freely distributed tool Inkscape with graph- ical user interface. We also provide a simple-to-use utility MapDataEx- tractor for transforming into a more appropriate format supported by our framework.
• Users can make use of framework’s pluggable design, allowing to configure the framework by adding new or altering existing files without the need to recompile the whole project.
• Players can save their gameplay for future playback. These replays are saved as a sequence of timed events – representing player’s input.
• The framework is multi-platform as it makes possible to develop games for both platform Windows and Android
• The whole second chapter of this thesis is dedicated to the user documentation, consisting of tutorials on how to configure our framework in order to create games.
• As a proof of concept, using our framework we created an illustrative example game. The game contains: two levels, four units, seven towers, eight game actions, four persistent upgrades, and six different user interface screens. This serves as a starting point for anyone willing to create games using our framework.
In the first chapter of the thesis we analyzed TD genre together with different approaches on how to design and implement certain parts of our framework. As a result of the analysis we set up an overview of high-level framework features which were later implemented during the development. User documentation in the second chapter serves as a reference for anyone using our framework. It is written as a sequence of tutorials with examples on how to perform certain tasks with our framework in order to create new games. Development documentation contains information about the solution design, its architecture and description of the key components of our framework. This part focuses on the implementation using C# programming language. In conclusion we can say that the project goals were fulfilled during the work on this thesis.
65 Future work
In this chapter we will mention some of possible improvements of our framework. Even though it was used to create an example game and therefore is functional, as with many other software projects there are usually possible improvements:
• Advanced time controlling: We already implemented the feature which allows players to skip to the next wave of enemies. However, being able to also slow-down or speed-up the time is certainly an interesting feature.
• Advanced scrolling camera: Our framework implements basic camera, supporting resolutions and aspect ratios of various devices. This might be combined with scrolling feature – to allow playing on bigger maps which would not fit on the screen.
• Game entity selection: Many real-time strategy games allow their players to select game objects and display their state and information about them. This could be a nice extension for the existing user interface and definitely appealing to many players.
• Catalog of game entities: It is common for some games to offer an user interface screen where players see the list of all game entities together with some information about them when selected.
• Extending scripting interfaces and introducing support for even more advanced game mechanics can be certainly interesting for many potential players but also users of the framework.
• Multicore systems are nowadays quite common even for mobile devices. Adding support for such systems would certainly result in better perfor- mance of our framework.
66 Bibliography
[1] List of open-source video games on Wikipedia. https://en.wikipedia.org/wiki/List of open-source video games. [Last accessed: 17.07.2017].
[2] Kingdom Rush. https://www.kingdomrush.com/home.html. [Last accessed: 17.07.2017].
[3] Bloons TD. https://ninjakiwi.com/Games/Tower-Defense/Bloons-Tower-Defense.html. [Last accessed: 17.07.2017].
[4] Plants vs Zombies. http://www.popcap.com/plants-vs-zombies. [Last accessed: 17.07.2017].
[5] Unity. https://unity3d.com/. [Last accessed: 17.07.2017].
[6] MonoGame. http://www.monogame.net/. [Last accessed: 17.07.2017].
[7] Unreal Engine 4. https://www.unrealengine.com/. [Last accessed: 17.07.2017].
[8] Google Play Store. https://play.google.com/store. [Last accessed: 17.07.2017].
[9] Technology tree. https://en.wikipedia.org/wiki/Technology tree. [Last accessed: 17.07.2017].
[10] Scalable Vector Graphics. https://www.w3.org/Graphics/SVG/. [Last accessed: 17.07.2017].
[11] Inkscape. https://inkscape.org/en/. [Last accessed: 17.07.2017].
[12] Markup languages. https://en.wikipedia.org/wiki/Markup language. [Last accessed: 17.07.2017].
[13] Scripting languages. http://en.wikipedia.org/wiki/Scripting language. [Last accessed: 17.07.2017].
[14] LUA. https://www.lua.org/, . [Last accessed: 17.07.2017].
[15] Game loop pattern. http://gameprogrammingpatterns.com/game-loop.html. [Last accessed: 17.07.2017].
[16] SharpDX. http://sharpdx.org/. [Last accessed: 17.07.2017].
[17] DirectX. http://en.wikipedia.org/wiki/DirectX. [Last accessed: 17.07.2017].
[18] OpenTK. https://opentk.github.io/, . [Last accessed: 17.07.2017].
[19] OpenGL. https://www.opengl.org/, . [Last accessed: 17.07.2017].
67 [20] XNA. https://msdn.microsoft.com/en-us/library/bb200104.aspx. [Last accessed: 17.07.2017]. [21] Wave engine. https://waveengine.net/. [Last accessed: 17.07.2017]. [22] Mono compiler. http://www.mono-project.com/docs/about-mono/languages/csharp/. [Last accessed: 17.07.2017]. [23] DPI. https://en.wikipedia.org/wiki/Dots per inch. [Last accessed: 17.07.2017]. [24] GeonBit.UI. https://github.com/RonenNess/GeonBit.UI. [Last accessed: 17.07.2017]. [25] Empty Keys. http://emptykeys.com/. [Last accessed: 17.07.2017]. [26] XAML. https://docs.microsoft.com/en- us/dotnet/framework/wpf/advanced/xaml-overview-wpf. [Last accessed: 17.07.2017]. [27] Model-View-Viewmodel. https://msdn.microsoft.com/en-us/library/hh848246.aspx. [Last accessed: 17.07.2017]. [28] NLua. http://nlua.org/. [Last accessed: 17.07.2017]. [29] MoonSharp. http://www.moonsharp.org/, . [Last accessed: 17.07.2017]. [30] LUA 5.2 Reference Manual. https://www.lua.org/manual/5.2/, . [Last accessed: 17.07.2017]. [31] MoonSharp addon for Visual Studio Code. https://www.nuget.org/packages/MoonSharp.Debugger.VsCode/, . [Last accessed: 17.07.2017]. [32] XNB format. http://xbox.create.msdn.com/en-US/sample/xnb format. [Last accessed: 17.07.2017]. [33] Nuget packages. https://www.nuget.org/. [Last accessed: 17.07.2017]. [34] Bin packing problems. https://en.wikipedia.org/wiki/Bin packing problem. [Last accessed: 17.07.2017]. [35] Garbage collection. https://en.wikipedia.org/wiki/Garbage collection (computer science). [Last accessed: 19.07.2017]. [36] Comma-separated values. https://en.wikipedia.org/wiki/Comma-separated values. [Last accessed: 17.07.2017]. [37] Common Language Runtime. https://en.wikipedia.org/wiki/Common Language Runtime. [Last accessed: 19.07.2017].
68 [38] Intermediate language. https://en.wikipedia.org/wiki/Bytecode. [Last accessed: 17.07.2017].
[39] Reiner “Tiles” Prokein. http://www.reinerstilesets.de/. [Last accessed: 17.07.2017].
69 List of Figures
1.1 One of levels from Kingdom Rush. Enemy units are raiding from the entrance at the bottom of the screen, trying to reach the path’s end at the top. Source: https:// www.kingdomrush.com/ .....5 1.2 An example of a grid-based TD game - Plants vs Zombies. The game map is divided into tiles on which can be built towers. The players are able to build certain towers which serve as obstacles. The enemy units are then forced to destroy them as they are blocking their path. Source: http:// www.popcap.com/ ...... 7
2.1 Example of file animations.tas ...... 18 2.2 Example of file effects.tas ...... 19 2.3 Example of file troop.tas ...... 20 2.4 Example of file towers.tas ...... 20 2.5 Example of file staticAssets.tas ...... 21 2.6 Example of file action.tas ...... 22 2.7 Example of file action.tas ...... 32 2.8 Example of a created map using program InkScape. The yellow rectangles represent the points where towers can be placed. The red rectangles represent the area where friendly units can be spawned. The connected line segments represent paths...... 34 2.9 Example of file levels.tas ...... 34 2.10 Example of a level definition – file level1.tas ...... 35 2.11 Example of file sounds.tas ...... 36 2.12 Screenshot from the example game. We can see the beginning of the level1. Raiding enemy units are trying to reach the end of paths at the bottom of the screen...... 40
3.1 Simplified object diagram of the solution. The diagram presents visualization of the key components and their relations across the solution...... 45 3.2 Flowchart visualizing game loop pattern ...... 46 3.3 Flowchart visualizing the initialization process of our framework and entering the game loop...... 48 3.4 Flowchart visualizing the flow of the main project after a level has been loaded. MonoGame periodically calls its update and draw methods...... 49 3.5 Class diagram of game entities ...... 51 3.6 Class diagram of some game objects ...... 52 3.7 Class diagram of parsers ...... 53 3.8 Class diagram of pointing devices ...... 54 3.9 Class diagram of game events ...... 56 3.10 Output file format of MapDataExtractor utility ...... 63
70 Attachments
Attachment, which can be found on the attached CD will be described in the following sections.
A Framework solution and documentation
In the folder \TowersAndSwords we can find the solution file TowersAndSwords.sln which loads every necessary project for the development for both Windows and Android platforms. In this folder we can also find the documentation of the framework’s project in the file docTowersAndSwords.chm. In the common subfolders we can also find compiled projects TowersAndSwordsAndroid.apk and TowersAndSwords.exe. In order for everything to work as intended, see the required tools for development in the section 3.1.
B MapDataExtractor utility solution
The solution file of the MapDataExtractor utility, MapDataExtractor.sln can be found in the folder \MapDataExtractor. Similarly, in its common subfolder we can find the compiled binary MapDataExtractor.exe.
C Compiling and running framework
To build for Windows, TowersAndSwords.csproj must be set as the startup project. To build for Android, TowersAndSwordsAndroid.csproj must be set as the startup project. Android specific options and manifest information canbe edited inside the Android’s project properties. Windows building process requires only to selected the Build option from the menu. This process generates all required files together with the main executable file TowersAndSwords.exe. Android application package can be generated by right-clicking the project, choosing the option Archive... and following further displayed instructions. After the process of creating a signed .apk file is finished, the package canbe published.
D Licenses
Used graphical assets: environment textures, animated game entities and visual effects, but also sound effects in the example game are created byReiner “Tiles” Prokein [39]. I would like to express my gratitude to him for making this content available for game developers. The framework and the example game are meant to be a contribution to the community and therefore is licensed with the MIT license.
71 Copyright (c) 2017 Andrej Cizmarik
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THESOFTWAREISPROVIDED"ASIS",WITHOUTWARRANTYOF ANYKIND,EXPRESSORIMPLIED,INCLUDINGBUTNOTLIMITED TOTHEWARRANTIESOFMERCHANTABILITY,FITNESSFOR APARTICULARPURPOSEANDNONINFRINGEMENT.INNOEVENT SHALLTHEAUTHORSORCOPYRIGHTHOLDERSBELIABLEFOR ANYCLAIM,DAMAGESOROTHERLIABILITY,WHETHERIN ANACTIONOFCONTRACT,TORTOROTHERWISE,ARISINGFROM, OUTOFORINCONNECTIONWITHTHESOFTWAREORTHEUSE OROTHERDEALINGSINTHESOFTWARE.
72