Developing a Football Game for Android - Markus Huhtamäki - Theseus
←
→
Page content transcription
If your browser does not render page correctly, please read the page content below
Developing a Football Game for Android Markus Huhtamäki BACHELOR’S THESIS March 2021 Degree Programme in ICT Engineering Software Engineering
ABSTRACT Tampereen ammattikorkeakoulu Tampere University of Applied Sciences Degree Programme in ICT Engineering Software Engineering HUHTAMÄKI, MARKUS: Developing a Football Game for Android Bachelor's thesis 57 pages March 2021 The objective of this thesis work was to develop a football game for the Android operating system. The game’s basic concept and its features were first outlined in a game design plan. In the game the user controls a player and tries to score goals against an AI-controlled goalkeeper. After considering different options, it was decided to develop the game without any designated game development tools. The game was developed using Android Studio with Java. GitHub was used for version control. For the game, an architecture with three Android activities, three threads and several custom classes was created. In-game physics and involving mathematics were developed for the movement and interaction of objects. A touchscreen- based controller was developed for moving the player. AI was developed for the computer-controlled goalkeeper. The original game design plan was successfully implemented in its entirety. Ideas for further development were also considered. The project started with only a basic understanding of Android development. There were many challenges and setbacks on the way that served as great learning experiences. Besides specific technical knowledge, key takeaways include a better understanding of how to plan an Android development project, how to better organize and maintain the codebase, and a better understanding on what additional value can be achieved by using game development frameworks. Key words: Android, game development, Java, touchscreen
TIIVISTELMÄ Tampereen ammattikorkeakoulu Tieto- ja viestintätekniikan tutkinto-ohjelma Ohjelmistotekniikka HUHTAMÄKI, MARKUS: Jalkapallopelin kehittäminen Androidille Opinnäytetyö 57 sivua Maaliskuu 2021 Opinnäytetyössä kehitettiin jalkapallopeli Android-käyttöjärjestelmälle. Aluksi tehtiin pelisuunnitelma, jossa hahmoteltiin pelin idea ja ominaisuudet. Pelissä käyttäjä ohjaa pelaajaa ja yrittää tehdä maaleja tekoälyn ohjaamaa maalivahtia vastaan. Eri vaihtoehtojen tutkimisen jälkeen peli päätettiin toteuttaa ilman varsinaisia pelikehitykseen tarkoitettuja työkaluja. Peli kehitettiin Javalla Android Studiota käyttäen. Versionhallintaan käytettiin GitHubia. Peliä varten luotiin kolmesta Android-aktiviteetista, kolmesta säikeestä ja useista eri luokista muodostuva arkkitehtuuri. Olioiden liikkumista ja keskinäistä vuorovaikutusta varten kehitettiin pelin sisäistä fysiikkaa sekä siihen liittyvää matematiikkaa. Pelaajan ohjaamista varten kehitettiin kosketusnäyttöä hyödyntävä ohjain. Tietokoneen ohjaaman maalivahdin tekoälyä kehitettiin. Lopulta kaikki alkuperäisestä suunnitelmasta saatiin toteutettua onnistuneesti. Myös jatkokehitysideoita pohdittiin. Projektin alkaessa Android-kehityksestä oli tiedossa vain perusasiat. Kehityksen aikana koettiin monia haasteita ja takaiskuja, jotka toimivat erinomaisina oppimiskokemuksina. Teknisen tietämyksen karttumisen lisäksi tärkeimpiä löydöksiä olivat parempi ymmärrys siitä, miten Android-kehitysprojekti kannattaa suunnitella, miten järjestää ja ylläpitää lähdekoodia ja mitä lisäarvoa pelikehitystyökaluilla voi saavuttaa. Asiasanat: Android, pelikehitys, Java, kosketusnäyttö
4 CONTENTS 1 INTRODUCTION .................................................................................. 7 2 GAME DESIGN PLAN .......................................................................... 8 2.1 Overview ........................................................................................ 8 2.2 Controlling the player ..................................................................... 9 2.3 Adjustable attributes..................................................................... 11 2.3.1 Outfield player attributes ..................................................... 11 2.3.2 Goalkeeper attributes ......................................................... 12 2.4 Rejected alternatives.................................................................... 13 3 ANDROID GAME DEVELOPMENT .................................................... 15 3.1 Mobile game development ........................................................... 15 3.2 Android application development ................................................. 16 3.3 Version control ............................................................................. 18 4 CHOSEN DEVELOPMENT TOOLS ................................................... 19 4.1 Android Studio.............................................................................. 19 4.2 Java ............................................................................................. 19 4.3 Git and GitHub ............................................................................. 20 5 GAME ARCHITECTURE .................................................................... 21 5.1 Activities and Layouts .................................................................. 21 5.2 Class Structure............................................................................. 24 5.3 Threads ........................................................................................ 25 5.4 Global Functions .......................................................................... 26 5.4.1 EpicalMath .......................................................................... 27 5.4.2 Collisions ............................................................................ 28 5.5 Global Constants.......................................................................... 28 6 GAME PHYSICS................................................................................. 30 6.1 Time management ....................................................................... 30 6.2 Dimensions and movement .......................................................... 32 6.3 Collisions ...................................................................................... 33 6.3.1 Moving objects with stationary objects ............................... 34 6.3.2 Ball with outfield player ....................................................... 35 6.3.3 Ball with goalkeeper ........................................................... 36 6.3.4 Players with each other ...................................................... 37 7 PLAYER CONTROLS ......................................................................... 38 7.1 Moving the player ......................................................................... 38 7.2 Pressing the shoot button ............................................................ 39 7.3 Releasing the shoot button .......................................................... 41
5 8 AI ........................................................................................................ 43 8.1 Positioning ................................................................................... 44 8.2 Saving .......................................................................................... 46 8.3 Intercepting .................................................................................. 48 8.4 Player on autopilot ....................................................................... 49 9 FURTHER DEVELOPMENT ............................................................... 51 10 DISCUSSION ..................................................................................... 53 REFERENCES ........................................................................................ 55
6 ABBREVIATIONS AND TERMS AI artificial intelligence Android an operating system for mobile devices Android Studio an integrated development environment for native Android development Cross-platform implemented on multiple computing platforms Game loop the part of the code that updates the game’s state in short time intervals Global accessible from all parts of the code IDE integrated development environment Java an object-oriented programming language Native development developing exclusively for a specific platform Touchscreen a display that receives input on touch Version control the practice of tracking and managing changes to software code
7 1 INTRODUCTION In recent years mobile games have become the most popular form of gaming (Green 2019). With global distribution platforms and an increasing number of free resources, it is easier than ever to design and publish games for world-wide markets. With free or low-cost game development tools even independent developers can create games with good production values in terms of graphics and features. The purpose of this thesis is to report the stages and outcome of developing a football game for the Android operating system. The objective was not to use any game specific development tools, but to create as much as possible from scratch using only default tools from Android Studio. The main challenges were in developing the application’s architecture, in-game physics, user controls and AI. As with all mobile development, the context of mobile devices and the conventions of the underlying operating system had to be considered. Also, as the game was developed with only mobiles phones in mind, the limitations of a smaller screen had to be taken into account. This affected especially the design of a touchscreen controller. The motivation for this project was to learn about mobile development for the Android operating system, especially from game development’s point of view. An object was also to lay groundwork for future game development projects. The game was developed independently and without an employer.
8 2 GAME DESIGN PLAN 2.1 Overview The purpose of this project was to design and develop a single-player football game for Android platform. The game received the working title Epical Football. In the game the user controls an outfield player and tries to score goals against an AI controlled goalkeeper. For controlling the player, a touchscreen-based controller was designed. When the application starts, an opening menu is shown. From the menu the user can adjust attributes for the outfield player and goalkeeper. Below the attributes is a button to start the match. During the match, the user will see (a portion of) a football field with the players and a goal. A total of 10 balls are fed to the field; whenever a ball goes out of bounds, is captured by the goalkeeper or a goal is scored, a new ball is fed until the balls run out. Above the field is shown the number of remaining balls and goals scored so far. Below the field is the section for controlling the player. The design for the match’s layout is shown in picture 1. When the match is finished, a result view is shown with the result of the match. From here the user can navigate back to the opening menu.
9 PICTURE 1. Match layout design Even though the game would be quite simple and would not even try to cover all the aspects of football, it should still portray the concepts of football within the game’s context in a meaningful way; in a sense, capture the essence of the sport. There should be an overall feel that everything is happening naturally and realistically; the movement of the players, the physics governing collisions (like the ball bouncing off a post or net), and the ball’s behaviour in the general. Also, the goalkeeper should behave intelligently; position itself well, try to intercept if the player dribbles too near, understand when to disengage not to wander too far from goal, and move properly when making saves. 2.2 Controlling the player The outfield player is controlled by pressing on the touchscreen-based controller section in picture 1. The player will start to accelerate to the direction relative to the center of the controller. The further from the center the touch is, the higher the speed the player will try to accelerate to. The bigger circle indicates the limit for maximum speed, and thus, pressing on it or outside it will result in accelerating to maximum speed. If the player had speed when the touch was released, it would
10 slow down to a halt. Pressing on the center of controller, on the dot, decelerates the player to a stop as quickly as possible. Besides the player moving on the field, there would also be visual feedback to the user from the controller. The pressed location would be shown as a dot on the screen, and the brightness of the dot would indicate the desired speed. In the case of decelerating, turning the center dot brighter would suffice. The outfield player controls the ball when touching and facing it at the same time. This interaction attempts to shift the ball all the time towards the player’s bearing, thus giving more control when moving and turning with the ball. The ball, however, is never stuck to the player in any sense, but both remain as their separate physical objects on the field – this is a very important consideration in making the ball behave realistically. Thus, if the player turns too quickly, the ball might slip out of control. When the shoot button is pressed (and held down), the player starts to gather power for the shot. The gathered power is indicated in the shot power meters. And when the button is released, the player will shoot the ball the next time it touches the ball (there is a time limit for this and exceeding that will cancel the shot). This allows for preparing for the shot even before being near the ball, which is important, for example, if the user wants to kick a ball that is moving towards the player. Optimally the best shot is released near the maximum of the power meters. But if the meters go full, the aim and speed of the shot start to suffer. The more time passes when the meters are already full, the worse the shot will be. This is indicated by turning the meters gradually red. And if enough time passes the shot is cancelled altogether. Besides taking care of the shot power, the user must also aim the shots. When the shoot button is pressed, the controller switches to an aiming view. In the aiming view a target goal moves and bounces off to different directions. The user needs to use the touchscreen to place a dot in a desired location of the target goal. The player will attempt to shoot the ball to the location where the dot was on the target goal upon releasing the shoot button. The aiming direction is also indicated on the field as an arrow from the player towards the intended target of the shot.
11 Designing a complex way of controlling the player’s actions required some amount of planning for the requirements of the controller’s usability. Naturally, the shoot button, as well as any other button in the application, should be sizeable enough that even a person with broader fingers could press them easily. Also, the controller should be sizeable enough to allow the user to give different desired speeds for the player, all the way from using the center dot to going for maximum speed. Furthermore, there should be some amount of buffer outside the maximum, as otherwise trying to touch for maximum speed would become too precise and difficult. 2.3 Adjustable attributes The game has nine adjustable attributes for the user-controlled outfield player and six for the goalkeeper. These will be described here briefly to give a better understanding of the game’s features. 2.3.1 Outfield player attributes Reach describes roughly how far the player can reach with the other leg extended. Thus, this attribute does not only signify the player’s size, but also its sphere of physical influence. This provides a more relevant interpretation of a performing athlete compared to just indicating its physical dimensions, as all the smaller movements, like reaching for the ball, is already built into the abstraction. Reach also determines how big the player is visually. Acceleration describes how effectively the player gains or reduces speed. Speed determines the player’s top speed. Ball Control describes the player’s ability to keep the ball under control while turning. For a player with poor ball control the ball might easily slip away when making sharp turns, while a more proficient player could make quite flamboyant
12 spins with the ball securely by its feet. Ball control also affects how well the player will stop the ball when receiving it. Dribbling describes the player’s ability to run fast with the ball in control. Shot Power determines the theoretical maximum speed for the player’s shots. Accuracy describes the player’s ability to accurately aim shots in the desired direction. Finishing and Long Shots are two attributes describing the player’s mental capability of creating good shots, visualizing the goal and concentrating on making a solid contact with the ball when kicking. Finishing would deal with close- range shots and long shots with long-range. 2.3.2 Goalkeeper attributes Reach for the goalkeeper is the same as with the outfield player, but with the difference that now it is be measured from the arms in a reaching position, thus making the goalkeepers’ spheres of influence bigger. Agility is the same as acceleration, but with the addition of making the goalkeeper’s recovery times shorter after making a save. Speed is identical with outfield player’s speed. Reflexes describes the goalkeeper’s reaction time when making a save. Ball Handling describes the goalkeeper’s ability to take the ball into possession instead of just bouncing it off when making a save. And if the goalkeeper could not hold onto the ball, this attribute would help direct the bounce into a more favourable direction.
13 Intelligence is the goalkeeper’s most important attribute. It describes its ability to position itself in front of the goal and make good decisions about how and when to try intercepting the ball. 2.4 Rejected alternatives Before starting the project and deciding that it would be solely for Android platform, other options were also considered. First there was the decision of whether to develop the game as a mobile application or as more traditional computer game. Mobile application was selected because it coincided better with the developer’s other studies and for the desire to learn more about mobile development. Developing solely for iOS platform was never considered, but a cross-platform solution was. Developing only for Android was selected because the technologies involved were already familiar to the developer. Also, some consideration was given to a possible publication of the application; in the cross-platform option two sets of publishing criteria would have to be met, which would have increased the workload. Mobile applications can run on both phones and tablets, and it can also be decided if it runs only on one of them. This is also a relevant decision as an application developed with tablets in mind could prove totally unusable for a normal phone. A tablet would have provided a much bigger screen and therefore less restrictions for the game’s layouts; this is something that a game with an isolated touch-screen controller would benefit from. But it was actually due to this reason that a normal phone sized screen was selected as a reference for development. Now all the layouts had to be designed with extreme precision, giving more opportunities to understand the minimum requirements of the visual and functional components on the screen. However, if the game would continue to be developed and expanded, and eventually published, it would be done only to tablets.
14 A multiplayer option was also considered, but this would have shifted a considerable amount of the project’s focus into setting up the server and developing the communication between the server and the mobile devices. As a result, there would have been less time and resources to develop the actual game, which would have been contrary to the aims of the project.
15 3 ANDROID GAME DEVELOPMENT 3.1 Mobile game development In the first decades of their development mobile phones were dedicated to performing phone calls, and due to high prices were used only by a very selected group of people (Loeffler 2021). It was in the 90’s that the evolution of mobile phones started to allow for more diversity in content, such as text messaging, calendars, and along with everything else, first mobile games. Notable landmarks include variants of Tetris in a few different devices, and Nokia’s Snake that became extremely popular (Paiva 2020). Even as mobile phones got more and more popular, the mobile game industry was stalling because the games needed to be pre-installed. It was first with the advent of the Wireless Application Protocol (WAP) and later with the digital distribution platforms, such as App Store and Google Play, that the mobile game development really took off (Paiva 2020). In recent years, mobile games have been the most popular form of video gaming, with prospects of an even further growth (Green 2019). With the distribution platforms and all the free resources available, nowadays even amateur developers can develop and publish mobile games for global markets without a considerable budget. With free game developing frameworks such as Unity, a developer can create complex and demanding games with relative ease (Sinicki 2020). Unity is a cross-platform game engine developed by Unity Technologies that offers many built-in features like a physics engine, 3D rendering and collision detection (Unity n.d.). Another example of a free game development tool is libGDX which is an open-source cross-platform Java game development framework. It also comes with a lot of resources for physics, graphics, and other important aspects of game development (libGDX: Features 2021). The alternative to using something like Unity or libGDX is to do everything from scratch. While this approach can be more laborious, it will give full control for
16 every aspect of the development. As an example, developing a physical model with moving objects, friction, collisions, and gravity could be quicker and easier with a built-in physics engine, but as a trade-off the engine might not cater to all ideas the developer has in mind. Also, if something does not wok as intended, it might be difficult to find the problem without a good understanding of the engine itself. An additional benefit in initially doing as much as possible yourself is to have a better comprehension of what problems game frameworks could solve in future projects. For a mobile application developer, it is good to bear in mind the uniqueness of mobile development and differences from more traditional software development. These include considering how different the contexts can be for using mobile applications compared to desktop computers for example, understanding the limitations and challenges of a touch-based user interface, and taking into account characteristic security threats of mobile technologies. Also, a mobile developer should be aware of the importance of user experience and the fact that most users are accustomed to the design standards of a given operating system. (Stangarone 2015.) 3.2 Android application development Android is an open source and Linux-based operating system for mobile devices such as smartphones and tablet computers and is primarily developed by Google (Android: What is Android n.d.). Over the years, Android has surpassed all its initial competitors, such as Symbian, Blackberry and Windows Mobile to become the most popular operating system for mobile devices (Callaham 2020). As it stands, Android’s market share is over 70 % with Apple’s iOS left as its only noteworthy competitor (Statista 2021). Along with many of its innovative features, one key reason for Android’s popularity was Google’s commitment to make it an open-source operating system, allowing it for free to third-party phone makers such as Samsung, LG and Sony (Callaham 2020). The popularity of Android has produced (and reciprocally benefited from) a thriving community of developers sharing wisdom
17 online. This includes numerous online resources, such as courses, articles, guides, and tutorials, some of which were used in this project also. One big difference – and a challenge – for Android developers is the sheer number of different kinds of devices the applications need to run on. Compared to iOS for example, with all the third-party manufacturers there are much more differences in screen sizes, pixel densities and device capabilities. With the release of Android Honeycomb, Android also started supporting large screen devices, such as tablets and smart TVs, which has further complicated the situation. (Hathibelagal 2016.) An additional challenge comes from the fact that most Android devices rarely receive software updates, if at all (Hathibelagal 2016). This means that an Android developer needs to support older versions of Android, as the most recent version might be running on just a fraction of all Android devices. The further back the support is given, the more devices the application will run on, but this might come at a cost in the form of restricting the development. Ultimately it is a question of optimization. Native Android development can be done with Kotlin, Java or C++ using the Android software development kit (Android SDK) (Android Developers: Application Fundamentals n.d.). The most popular choice for an integrated development environment (IDE) for native development is Android Studio (Android Developers: Meet Android Studio n.d.), with other options include Eclipse and IntelliJ for example. Besides native development, applications can also be developed to be published on multiple operating systems. This is offered through hybrid or cross-platform technologies such as Flutter, React Native and Xamarin. The benefit of this is to avoid developing the same application separately for different operating systems. As a drawback, non-native applications suffer from poorer performance. Also, not all third-party libraries work in sync with the cross-platform development frameworks. (Klubnikin 2017.)
18 Android applications can be published and distributed via several different Android application marketplaces, with Google Play being the most prominent. With Google Play anyone who has registered for a developer account can submit an application for publishing by providing the program file along with some necessary details. The application will be published after going through the approval process. (Hathibelagal 2016.) 3.3 Version control One key aspect of software development is version control. Version control, also known as source control, is the practice of tracking and managing changes to software code, and version control systems are software tools that help software teams manage changes to source code over time (Atlassian: What is version control? n.d.). Version control offers many benefits, including tracking the history and evolution of the project, making collaboration easier by allowing simultaneous development and aiding in resolving conflicting versions, and, in case of an error, providing an easier way to revert to earlier versions (O'Sullivan 2006). Version control is imperative with many people involved, but even a single person can benefit from it for the same reasons (O'Sullivan 2006). One other reason, that is not so often mentioned, is simply to have the code stored in another location besides the local computer. In case of an accident or a stolen laptop, the whole project could be lost. In addition, even a project that starts with only one person might later involve other people as well. Different options for version control include Git, Apache Subversion (SVN), Mercurial, Concurrent Versions System (CVS), Perforce, GNU Bazaar and many more.
19 4 CHOSEN DEVELOPMENT TOOLS 4.1 Android Studio Android Studio is Google’s official IDE for Android app development (Android Developers: Meet Android Studio n.d.). It was mainly chosen as the IDE for its good features and usability. The features include, for example, the possibility to easily test the applications with both a built-in emulator and a separate mobile device, and a GitHub integration. In general, the clearest disadvantage of Android Studio is that it supports only native Android development. But since there was no plan to go cross-platform, this was not an issue. In fact, it made the choice even more natural as the necessary tools for Android development were already bundled in. Android Studio was also familiar to the developer beforehand, thus easing the learning curve for the project. Using Unity or libGDX would have definitely provided a readier toolset for making games, especially in terms of graphics and in-game physics. One great motivation for the project was however to design as much as possible from scratch. Only Android’s default packages were used. For example, all the graphics in the game (besides background images) are simple figures drawn using Android Graphics. Android Graphics is an API package that provides low- level graphics tools for drawing to the screen directly (Android Developers: Android Graphics n.d.). 4.2 Java Android apps can be written using Kotlin, Java, and C++ languages (Android Developers: Application Fundamentals n.d.). Even though Google has lately adopted a Kotlin-first policy, Java has been an official programming language for Android development for a longer time (Sinicki 2019). Java is an open-source object-oriented language owned by Oracle (w3schools.com: Java Introduction n.d.). It is one of the most popular programming languages in the world, and thus offers an extensive community support. With the assumption that there would be
20 more online material available for developing Android with Java, it was selected as the programming language for this project. 4.3 Git and GitHub Since Android Studio already had an integration for it, choosing Git and GitHub for version control was a natural choice. Git, developed by the Linux Foundation, is by far the most widely used modern version control system in the world today (Atlassian: What is Git n.d.). Git is a free and open-source distributed version control system designed to handle many kinds of projects. Microsoft’s GitHub, on the other hand, is a code hosting platform for collaboration and version control that uses Git (w3schools.com: What is GitHub? n.d.) The basic workflow was to commit code changes, whether they were about adding new features or fixing bugs, as separate commits to GitHub. Also, as the code was always safely stored in the GitHub repository, it could be retrieved in problematic situations for a do-over.
21 5 GAME ARCHITECTURE 5.1 Activities and Layouts In the core of Android applications are the Activity classes. An activity represents a single screen with a user interface (Android Developers: Activity n.d.). When an application is launched, an activity (usually the designated main activity) is launched to provide the user with a user interface and to handle its input and output (Android Developers: Introduction to Activities n.d.). Thus, an activity can be seen as the starting point of the program code. An application can have multiple activities and they can pass on the control to each other (Android Developers: Introduction to Activities n.d.). Three activities were created per the need for three screens in the game design plan: MenuActivity, MatchActivity and ResultActivity. For these activities necessary layouts were created. A layout defines the structure for a user interface in the application (Android Developers: Layouts n.d.). For the menu two layouts were created as there was a need to switch between setting the player and goalkeeper attributes. When the application is launched the menu with the player attributes is shown to the user (picture 2). Below the title there are two buttons that the user can use to switch between the player and goalkeeper attributes. The layout for the goalkeeper attributes can be seen in picture 3. The text on the button is bolded to signal which attributes are shown. The user can set the value of each attribute from the plus and minus buttons. The start game button at the bottom starts the match.
22 PICTURE 2. Menu view with attribute settings for the player PICTURE 3. Menu view with attribute settings for the goalkeeper The view of the match can be seen in picture 4. At the top there are two numbers representing the balls that are left for the player and the scored goals so far. Below is a view of the playing field. The outfield player (blue) is moving with the
23 ball and the goalkeeper (red) is guarding the goal. Below the field is the control section. In the middle is the controller that the user can use to move the player. The magenta dot shows where the user is touching the screen. On either side of the controller are power bars that indicate the power of a shot. At the bottom is a button to commence aiming and shooting. PICTURE 4. Match view After the user has used all the given balls, the match ends and the result is shown in the result view (picture 5). Pressing the button at the bottom of the result view takes the user back to the menu.
24 PICTURE 5. Result view 5.2 Class Structure Several custom classes were created to implement the actual match. Most of these are shown in figure 1. A possible parent class (inheritance) is written inside brackets. For example, OutfieldPlayer class inherits Player class which in turn inherits Circle class. Circle class was created as a parent to all circular objects, and similarly Android Graphics’ RectF was used as a parent to all rectangular objects. In addition to the ones in the figure, Position and Vector classes were created to be used by any object with a position or movement on the field, EpicalMath and Collisions classes were created to offer static functions, and Constans to offer global constants.
25 FIGURE 1. Class architecture The arrows in figure 1 show what classes a given class calls to create new objects. Thus, we can see how the match activity is the starting point for initiating the match. The match activity creates an instance of MatchState that holds all the functionality of the match, and an instance of MatchSurfaceView that is used to draw the match’s graphics on the screen, and these go on to create all the other necessary objects. The figure does not show dependencies between classes; for example, besides the match activity also match surface view and match runner need to use the created match state object directly. This makes the whole architecture more complex and intertwined. Figuring out the hierarchy and dependencies between classes was one of the key issues when designing the application, and ultimately one of the project’s biggest successes. 5.3 Threads When an Android application is launched, the Android system starts a new Linux process for the application with a single thread of execution (Android Developers: Processes and Threads Overview n.d.). A thread can be thought of as a worker
26 for the process (Beatteay 2018). The thread started for the process by default is called the “main” thread (Android Developers: Processes and Threads Overview n.d.). The application can be made more efficient by creating new threads inside the main thread and sharing the workload of the process between the threads. This is called multithreading (Beatteay 2018). However, as switching control between threads also takes time, using multithreading in a simple application might actually slow it down (Beatteay 2018). Regardless of its impact on efficiency, most Android game development tutorials and examples found online use at least a separate thread for the game loop. As some of the game loop’s operations might momentarily block the thread, it is better to run it in a separately thus keeping the main thread free to process user input and output; this makes the application more responsive (Goshen n.d.). Following the example of Cave of Programming’s tutorial (2016), a separate thread for the game loop is created (as an instance of the MatchRunner class) when the match surface view is created. Additionally, one more thread was designed to handle the decisions of the AI controlled goalkeeper (an instance of the AIRunner class). This in turn is created when the match state is created. In terms of efficiency or responsivity there was no need for this thread, and it was designed solely for educational purposes. However, if the game would eventually be expanded to have several AI players with complex decision algorithms, using a dedicated thread for AI would be completely justified. 5.4 Global Functions Global functions, or functions that could be used anywhere in the program, were offered as static functions from two classes EpicalMath and Collisions. EpicalMath was designed to offer functions for all the necessary mathematical calculations for the game, and Collisions was designed to offer functions for all interactions between objects on the football field. Although technically not meeting the criteria of libraries, in a practical sense these classes were used as custom libraries.
27 5.4.1 EpicalMath In the early stages of developing the program, mathematical calculations were written directly to their place in the code or occasionally separated as functions to be used only in that one class. But as the mathematical modelling became more ambitious and calculations grew in terms of code lines, and as similar calculations started to appear in different parts of the program, it became evident that the code needed to be reorganized. The solution was to offer mathematical calculations globally from a separate class as static functions. This class was named EpicalMath. The mathematical functions gathered in EpicalMath mostly dealt with analytic geometry, aiding in calculating directions, distances, positions and checking potential intersections on the two-dimensional field; for example, determining the distance between two coordinate positions, calculating a new direction if a given direction was shifted towards a reference direction, or as shown in source code 1, determining the midway point of two given positions. public static Position getPositionBetweenPositions(Position position1, Position position2) { float x = (position1.getX() + position2.getX()) / 2; float y = (position1.getY() + position2.getY()) / 2; return new Position(x, y); } SOURCE CODE 1. Determining the midway point of two given positions Besides making the code clearer, another advantage of separating calculations to their own class was that those calculations could now be easily used in other projects as well. In fact, this has laid groundwork for creating a mathematics library for games with a similar approach to using moving objects in a two- dimensional plane. However, there was still many calculations in the code, mostly trigonometrical in nature, that with a different kind of mindset could have also been abstracted and moved to EpicalMath. In fact, one of the key takeaways from this project was to recognize the value developing separate classes with static functions; with better planning and a more consistent approach, the quality of the
28 code would have been better and one of the outcomes would have been a solid groundwork for a mathematics library for two-dimensional games. 5.4.2 Collisions In similar fashion, Collisions was designed to take care of all the interaction between objects when they intersect with each other. There was a need to write a separate collision function to almost all possible combinations of participants. For example, the ball would react differently when colliding with the outfield player, goalkeeper, goal post or goal net, but also the outfield player would react different when colliding with the goalkeeper, goal post or goal net. As there was a certain ambition to make the events seem realistic, and thus many things needed to be taken into consideration, the functions grew quite long and complex. Especially the interaction between the ball and the outfield player had to entail, besides just bouncing the ball off properly, the ways the player could influence the ball via dribbling, shooting or controlling it. Developing, testing and improving these collision functions was one of the most arduous segments of the whole project. If the purpose for EpicalMath was to lay groundwork for a mathematics library for two-dimensional games, Collisions could have done the same for a physics library. But the way of handling the collisions became very particular for this game and not nearly as universal, and therefore not as transferable to other games. And since only few classes ended up handling collisions, there was no need to offer them as static. In retrospect, offering the collisions from a normal class would have sufficed and would have been a better solution. 5.5 Global Constants In the early stages of development many constants were written directly in the code (hard coded). As it became increasingly harder to locate them, there was an attempt to move some of them to the beginning of classes. This approach still
29 left a few problems; for example, if the game wanted to be tested with a different set of constant values, changes needed to be made to multiple files. Furthermore, some of these values were supposed to be the same across different classes. A need for a separate file containing all constant values was recognized. A class called Constants was created and all the values were gathered there to be offered as global static values. Now everything could be controlled from one class. In addition to values that were used to configure the game’s behaviour (such as the radius of a ball, or what shape of speed curve and accelerating player has), also often used mathematical values were included, such as the radian value of the direction left or values of often used numbers. The class ended up having over two hundred constants and as a result the code became much more readable.
30 6 GAME PHYSICS 6.1 Time management One important component of computer games is the game loop that updates the game’s state in cycles. The key question in designing the game loop was how to set the time intervals so the game would behave optimally. Different ideas from various sources were examined for this project, but all of them seemed to have shortcomings. In the end, an original logic was developed in an attempt to combine the best ideas while discarding the shortcomings. A simple game loop would update the game’s state in fixed increments. That is to say, in every cycle an object moving at a constant speed would move the same amount. The benefit of this would be that the code would become much simpler as, comparing to the case where the increments would be relative, the size of the increments would not have to be considered at all. The game’s speed could be adjusted by having the thread sleep for a moment in every cycle; the length of sleep would determine how fast or slow the game would advance. The problem with this is, however, that how the game runs would depend on the device’s performance; the speed of the game would alter all the time depending on how much resources the device has for running it. Also, a well-performing device would run the game faster compared to slower devices. This is the reason why using relative increments is better, and why the length of the increments (usually measured in milliseconds) must be considered in the code when updating the game’s state. One solution was to give the increments as the length of how much time has elapsed from the last time the game’s state was updated. Theoretically, this will keep the game running smoothly. Because this would make the loop run as fast as possible, heavily taxing the device’s performance and battery, a good addition is to, once again, make the thread sleep for a moment every cycle.
31 This game loop is far better than the first one but has problems too. Because the lengths of increments are not controlled in any way, in turn, they would vary too widely depending on the device’s performance. For example, when a ball advances towards a goal post, the length of the increments will determine how far it can travel inside the post before a collision is detected and handled. This would make the game’s physics behave differently depending on the device’s performance. Another problem is that the sleep serves no purpose if a relatively long time has passed after the last update. The game loop developed this for game used both described approaches. The game’s state is updated by the elapsed time from the last update, thus making the game run smoothly. But as an addition the length of sleep is set by how much time has passed to run the current cycle, making the cycles almost identical in duration, solving the problem in the game’s physics. The final form of the game loop is in source code 2. long lastTime = System.currentTimeMillis(); long elapsed; long begin; long delta; while(running) { begin = System.currentTimeMillis(); elapsed = begin - lastTime; lastTime = begin; matchState.updateGameState(elapsed); matchSurfaceView.drawOnSurface(); delta = System.currentTimeMillis() - begin; if (delta < GAME_UPDATE_TIME) { try { Thread.sleep(GAME_UPDATE_TIME - delta); } catch (InterruptedException e) { e.printStackTrace(); } } } SOURCE CODE 2. The game loop The constant GAME_UPDATE_TIME is the target time for the cycles’ durations. If the time executing the cycle exceeds this, the sleep is skipped, once again introducing the problem of varying increments. But when the constant is selected sensibly, it will cover almost all situations ensuring a smooth gameplay. It was set at 30 ms.
32 An additional precaution would have been to set an upper limit for the time increment to be applied. This would have been to ensure that if the device is not functioning properly, the game would not update at all. This would momentarily freeze the game until normal conditions were resumed. Freezing the game would be more user-friendly than to let the game run when it is basically unplayable; if the time increments are too long, all movement will be choppy, and the game’s physics will not behave as intended. The time increments of approximately 30 ms proved to be satisfactory for most situations of the field. However, when the ball was moving very fast, the time increment was too long. Especially, as a really fast ball can travel over a meter in that time frame, it might skip through the nets and posts that had to be kept reasonably narrow. The solution was to process everything else first, then process everything to do with the ball separately by dividing the time increment into even smaller increments. This made the ball move in extremely short steps. Another solution would have been to make the whole cycle run in smaller increments, but as there is a limit on how quick cycles the device can perform, and as the ball was the only perpetrator, this would have been a poor solution. This logic of dividing the time increment in fractions could have also been used selectively to combat time increments that are too long. For example, when the match state’s update is about to ensue, the length of the time increment would be checked and then decided in how many smaller pieces to divide it. This would have solved the case of ill-behaving physics but not choppy graphics, as updating the graphics would still be tied to the original time increment. Ultimately, probably the best solution would be to combine both of the ideas: to start dividing the increments when they escalate and to freeze the game if they get too long. 6.2 Dimensions and movement With no height dimension – or depth from the user’s point of view – the game is both visually and structurally completely two-dimensional. The metrics of the field were coded in the constants file. In other parts of the code, the goal’s width and
33 the penalty box’s height were used as references, and all the logic worked in relation to them. Thus, even with different values than the official 7.32 m and 16.5 m, the game would still work, including building the goal frame from nets and posts and how the goalkeeper’s AI functions. To have the metrics correspond to the background image, the width of the goal, height of the penalty box and the height from the top to the touchline were calculated from the image along with the total height and width of the image as amounts of pixels. Setting the touchline as the x-axis, with readily made formulas only these pixel amounts needed to be set in the constants and all the visuals locked into place. This was very convenient when auditioning different background images. All the positions on the field are expressed as x and y coordinates, and all the speeds and accelerations as vectors with direction and magnitude. The first step of moving an object is to determine its acceleration. All objects have a base deceleration to slow them down by default, and otherwise the players’ acceleration come from either user controls or AI (depending on the relevant attributes as well). The next step is to update the speed with the acceleration by the given time increment. And finally, to update the position with the speed by the given time increment. 6.3 Collisions One key aspect of the game’s physics was to determine how the objects would interact with each other when coming into contact. These collisions are detected by checking every cycle whether the objects intersect. For example, if the ball intersects with the goalkeeper, or the outfield player intersects with any of the goal nets or posts. Most of these instances needed to be designed separately, but in some cases, the same method is used for different combinations of objects. Naturally, there is no need to check collisions between stationary objects. One vital decision regarding the realism of the physical model was whether to implement inertia. As the game was to be relatively simple and the ball’s mass
34 would be relatively insignificant comparing the mass of the players, it was decided that inertia would not be considered. 6.3.1 Moving objects with stationary objects The collisions between the outfield player or the goalkeeper with rectangles like goal nets and the field’s outer boundaries could be handled with the same collision method. To put it simply, first is checked from which side of the rectangle the player tries to penetrate it, then the player is instantly moved outside the rectangle to that direction, and finally the component of the player’s speed to that direction is removed. This will make the player stop or slide along the rectangle’s side without ever ending up inside it. For the ball the process is similar but, instead of reducing a component of the speed, it is bounced off with reduced speed. The faster the ball’s original speed, the more it was reduced; this would make the nets behave in a less elastic way. In terms of physics, this model was satisfactory. However, since the goalkeeper’s AI was not sophisticated enough to plot routes, it would get stuck running against the net if ending up to the wrong side of it with the ball. As a shortcut, the goalkeeper is made to slide down every time it touches a net. This would hasten its return to the field. The handling of collisions between the players and the goal posts are similar to those with nets, except now the objects are both circles. The speed component towards the center of the post is removed, but instead of just moving the player outside of the post, it is also shifted a little towards the field. The reason for this was that it was easy for the player to get stuck in the nearly invisible “pocket” between the net and the post, and this helps to guide the player away from it. For the goalkeeper this is necessary for both this reason and for the reason with the nets. With the ball, again instead of removing the speed component towards the post its direction is bounced off the post and speed reduced. Additionally, this time
35 both the original speed and the angle of the collision would determine how much the speed is reduced. The method handling the collision between the ball and a goal post is shown in source code 3. It also illustrates the use of EpicalMath and the global constants. public static void handleBallGoalPostCollision(GoalPost goalPost, Ball ball) { if (EpicalMath.checkIntersect(goalPost, ball)) { float radiusSum = goalPost.getRadius() + ball.getRadius(); float goalPostToBallDirection = EpicalMath.convertToDirection(goalPost.getPosition(), ball.getPosition()); float goalPostToBallBallSpeedAngle = EpicalMath.absoluteAngleBetweenDirections( goalPostToBallDirection, ball.getSpeed().getDirection() ); if (goalPostToBallBallSpeedAngle > QUARTER_CIRCLE) { ball.getSpeed().bounceDirection(goalPostToBallDirection); float multiplierAngle = goalPostToBallBallSpeedAngle - QUARTER_CIRCLE; float speedMultiplier = FULL - (float)Math.sin(multiplierAngle) * (FULL - GOAL_POST_COLLISION_SPEED_MULTIPLIER); ball.getSpeed().setMagnitude(speedMultiplier * ball.getSpeed().getMagnitude() * (FULL - ball.getSpeed().getMagnitude() / BALL_COLLISION_REFERENCE_MAX_SPEED * BALL_COLLISION_SPEED_REDUCTION_BY_SPEED_FACTOR)); } ball.getPosition().copyFromPosition(goalPost.getPosition()); ball.getPosition().addPositionVector(goalPostToBallDirection, radiusSum); } } SOURCE CODE 3. Collision between the ball and a goal post 6.3.2 Ball with outfield player The interaction between the ball and the outfield player turned out to be of the most complicated pieces of the game. Whether the player can shoot is checked first. If this is true, handling of the collision return true and the shot is handled elsewhere, otherwise the method continues. Next, whether the player and the ball are moving towards each other or whether the player is either touching a stationary ball or gaining on the ball that is moving away from it is determined. In case of moving towards each other a bounce occurs and the ball’s speed is decreased. If the ball bounces when the player is facing the ball, the speed’s reduction along with a small shift to the bounce’s angle depend on the player’s ball control attribute – so more skilful players have more control in receiving the ball. If the ball bounces and the player is not facing the
36 ball the reduction depends on the ball’s speed and the collisions angle, as with goal posts (only that players are softer and reduce the speed more). Furthermore, in all cases the impulse caused by the player’s own movement is added to the ball’s movement. And finally, the ball is always moved out of the player’s sphere. If the player moves constantly towards the ball, the impulse caused by the player keeps moving the ball forwards, thus making dribbling possible. However, if there is nothing to keep the ball in front of the player, it would simply slip aside. And since after the collision the ball is moved outside the player, the contact is automatically broken, making having the ball follow the player’s intentions while turning practically impossible. This was solved by adding a “control cone” as an extension of the player’s “face”. The reach of the cone depends on the ball control attribute. Whenever the ball was intersecting with this cone, the player would impose some amount of control to the ball, depending on the player’s ball control attribute. The cone constantly shifts the ball both closer to the player and nearer the direction the player is facing. This makes dribbling and turning with the ball far easier. However, the ball can still slip away if the turn is too sudden especially with players of poor ball control. Also, when the player is dribbling, its maximum speed is reduced depending on its dribbling attribute. 6.3.3 Ball with goalkeeper As with the outfield player, the interaction between the ball and the goalkeeper is quite complicated. One outcome of the collision is that the goalkeeper captures the ball, thus ending the play until a new is fed. This would occur if in the collision the ball is standing still, the goalkeeper gains on a moving ball or the goalkeeper makes a successful save. Also, in the special case of the goalkeeper touching the ball outside of the penalty box it would only kick the ball away (as per the rules it cannot touch the ball with its hands). When the collision occurs inside the box and the ball is moving towards the goalkeeper, the goalkeeper attempts to capture the ball. A somewhat complex
You can also read