Software Engineering Fundamentals to Design Application for Modern Game Engines Viacheslav Bezditnyi1, Olena Chebanyuk2 1 Igor Sikorsky Kyiv Polytechnic Institute, Beresteiskyi Ave, 37, Kyiv, 03056, Ukraine 2 National Aviation University, 1, Liubomyra Huzara ave., 03058, Kyiv, Ukraine Abstract The article outlines a methodology for designing multimedia applications for game engines using a component-oriented architectural style. It highlights the importance of selecting the appropriate architectural pattern, considering the features of modern game engines such as Unity, Unreal Engine, and Godot Engine. Key challenges and issues in designing flexible and scalable architectures are addressed. The life cycle model of the user interaction session with the application is described, comprising three main stages: Bootstrap, GameLoop, and Dispose. The article emphasizes the significance of managing transient processes between life cycle states to ensure the application's proper functioning. To better organize service interactions, the mathematical framework of category theory and set theory is applied, allowing for clear definition and management of dependencies and relationships between components. The benefits of using category theory for modeling and managing data flows and dependencies in the system, particularly through functors and monads, are discussed. These methods facilitate the creation of adaptive and scalable systems that are easy to maintain and extend. The article provides practical recommendations for designing the architecture of game applications, considering the specifics of component-oriented and service-oriented architectural styles. It underscores the need for regular review and updates of the project architecture in response to changing requirements and operating conditions. A proposed methodology serves as a theoretical foundation for developing flexible architectures of multimedia applications, accounting for the specifics of component-oriented and service-oriented architectural styles and the functioning of game engines. The work includes examples of practical applications of the proposed theoretical approaches in real projects, demonstrating their effectiveness and applicability in various game development contexts. These examples feature concrete implementations of patterns, state management using the State Machine, and optimization of component interactions through Service Locator and Dependency Injection. Keywords 1 Game Engine, Design Patterns, Unity, Service-Oriented Architecture, Theory of Categories, Component- Oriented Architectural Style, Service locator 1. Introduction Earlier the approaches to design graphical applications used the same designing principles as designing of web and other types of applications [1]. Designing of multimedia applications require special approaches for interface designing. Classical Model-Driven Development methodologies needed to be adopted for development of applications that need component-oriented [2], service-oriented [3, 4], agent-oriented [5], and other architectural styles [6.7]. Also modern multimedia applications support complex UI, and usual software designing approaches based on UML diagrams cannot effectively reflect all the details of multimedia applications. Special approaches, considering the peculiarities of different GameObject structures, various types of game scenes (2D, 2.5D, and 3D scenes), and the assessment of script quality attached to 14th International Scientific and Practical Conference from Programming UkrPROG’2024, May 14-15, 2024, Kyiv, Ukraine Corresponding author. † These authors contributed equally. vyachbezd@lll.kpi.ua (V. Bezditnyi); chebanyuk.olena@gmail.com (O. Chebanyuk) 0009-0002-7319-964X (V. Bezditnyi); 0000-0002-9873-6010 (O. Chebanyuk) © 2024 Copyright for this paper by its authors. Use permitted under Creative Commons License Attribution 4.0 International (CC BY 4.0). CEUR ceur-ws.org Workshop ISSN 1613-0073 Proceedings GameObjects, may be adopted for designing of multimedia applications [8]. The same problem is actual for the designing of VR? AR, and mixed reality applications [9]. The modern game industry is constantly developing, offering more and more complex and innovative solutions in the creation of game applications. One of the key aspects of effective game development is choosing an architecture that provides flexibility, scalability, and ease of code maintenance. The basis of a game application is a game engine, which is an environment for developing and prototyping game applications with its own features and programming language. Among the popular game engines, Unity Engine, Unreal Engine, and Godot Engine are most often singled out (Fig. 1) [10]. Figure 1: The most popular game engines in the world. Figure is taken from [1]. 2. First Challenges and Issues in Designing Flexible Architectures for Modern Game Engines Designing the architecture of any application can pose certain challenges and problems, in particular: 1. Choosing an appropriate architectural pattern: Game engines do not impose strict restrictions on the architecture, which can lead to difficulties in choosing the optimal pattern. Common patterns such as MVC (Model-View-Controller), MVVM (Model-View- ViewModel) or ECS (Entity Component System) have their advantages and disadvantages depending on the project [11]. 2. Initial architectural decisions can make it difficult to scale the project in the future. The wrong choice can lead to the need to rewrite the code when adding new functions [12]. 3. Using the available functionality of game engines without a designed architecture and without using best practices leads to the creation of tightly coupled systems, which complicates their testing and refactoring [13]. Using SOLID principles and design patterns such as Dependency Injection, Service Locator can help manage these dependencies. 4. An architecture that complicates testing can significantly increase development time. Automated testing such as unit tests can be difficult to implement in Unity due to game engine dependencies [6]. 5. Some architectural patterns can negatively affect performance, especially in projects with a large number of objects and components. Optimization and proper pattern selection are key to maintaining high performance. Integration with external systems such as databases or web services can be difficult depending on the chosen architecture. It is important to plan these aspects in the early stages of development. Different developers may have different approaches to architecture, which can make team collaboration difficult. It is important to establish clear standards and principles of architecture at the beginning of the project. To solve these problems, it is important to spend time planning the architecture, regularly reviewing and updating it according to changes in the design, and taking into account the experiences and best practices of other developers. Today, there are a large number of patterns and implementations of their combinations. In order to choose the most suitable architecture, it is suggested to consider what challenges developers have to deal with. The first is getting dependencies from another class. This is usually solved by setting up relationships between components in the development environment, using the Singleton pattern, static fields, or events [14]. The disadvantages of this approach become noticeable in the process of filling the project with objects and logic. An alternative is Dependency Injection (DI), which allows you to pass a reference to an object through a dependency injection mechanism [15]. Another challenge is maintaining the structure of the project, minimizing problems with the initialization order. For this, you need to clearly control the life cycle of the application, as well as game objects. The State pattern is suitable for defining life cycle stages, since each stage is a separate state of the application that we can enter and exit. In addition to dependencies, developers often face the question of how to use the same functionality in different parts of the project? If the logic is global for all users and 100 users make requests to one service at the same time, then at best only a few will get the result. This problem is solved by the Service Locator pattern in combination with the State pattern to ensure sequential execution of requests in the order determined by the state [10, 11, 6]. Let's take a closer look at the states and description of the game cycle of the application. 3. Technologies Enhancing Gaming Application Functionality For any application, such as an educational app or a game, the session life cycle typically follows this structure (see Figure 2): Bootstrap GameLoop Dispose Transition Transition processes processes Figure 2: Model of the user interaction session life cycle with the application. Bootstrap is an entry point, a place where services, dependencies are initialized, resources necessary for launch are loaded. GameLoop is directly the game process, interaction with the application, in which all game or business logic takes place. Dispose is the state of the application before exiting, unloading resources, services, etc. The most critical points in this scheme (Fig. 2) are between the states of the life cycle and can be called "transitional processes". This means, for example, the order of initialization of services, verification of obtaining dependencies, the order of unloading resources from memory. If these processes are left unchecked, we get incorrect program execution or even errors that block the execution process. For the correct operation of the application, we need to control these states, have a convenient mechanism for transition between them, have an initial entry point in which the necessary dependencies are initialized in a controlled manner using services. And access to the services themselves is implemented using Dependency Injection. 4. A formal model of the gaming process, utilizing discrete mathematics principles In order to interpret the game cycle into a mathematical model, it is possible to use the Theory of Category. Consider the main analogies: • Objects and Morphisms: In category theory, systems and their interactions can be modeled using objects (components, services) and morphisms (functions that describe interactions between these objects). In the context of Unity, this can be used to define the relationships between various game components, such as the Service Locator, DI, Factory, and State Machine. • Functors and Monads: Functors (structures that map objects and morphisms of one category to another) and monads (a type of functor that allows sequentially combining operations) can be used to model and manage data flows and dependencies in a system. • Sets and Subsets: Game elements (objects, components) can be represented as sets, and their properties and characteristics as subsets. This allows you to clearly define and manage dependencies and relationships between components. • Operations on Sets: Operations such as union, intersection, and difference of sets can be used to describe interactions between components. For example, combining sets can represent the integration of different services into one system. Application in Unity: • Service Locator: This can be represented as a centralized system responsible for tracking and providing access to various services (objects), using the principles of category theory to manage dependencies. • Dependency Injection: This can be interpreted through set theory, where dependencies between objects are represented as relations between sets. • Factory Pattern: Used to create objects, which can be viewed through the lens of category theory, where a factory is a functor that maps parameters to objects. • State Machine: This can be modeled using set theory, where states are represented as sets and transitions are represented as relationships between them. • Extended Application and Benefits: • Scene Management: Category theory can manage scenes and transitions in Unity, treating each scene as an object and transitions as morphisms. • AI Systems: AI behaviors and decision-making processes can be modeled using functors and monads to manage state and behavior transitions. • Performance Optimization: By formalizing interactions and dependencies, category theory can help identify and optimize performance bottlenecks. • Scalability and Modularity: These theoretical approaches promote modularity and scalability, making it easier to update, test, and maintain the system. • Real-World Examples: For instance, consider a Unity project where the service locator pattern is used to manage audio, saving/loading, and networking services. Each service is an object, and interactions between services are morphisms. Using dependency injection, the audio service can be easily replaced without modifying the networking code. Challenges and Considerations: Complexity: Implementing these concepts can be complex and may require a steep learning curve for developers unfamiliar with category theory. Tools and Libraries: Utilize libraries and tools that support functional programming and dependency injection to facilitate the implementation of these concepts. Integration: Ensure these theoretical approaches integrate well with Unity’s existing architecture and components. By combining the theoretical approaches listed above, it is possible to develop a flexible and scalable system for Unity that optimizes the management of dependencies and data flows in complex projects. The formalism provided by category theory and set theory can help in designing robust and maintainable game architectures, enhancing both development and performance. 5. Model of Game Application Functionality States and state management are typically implemented using the Game State Machine pattern. A "State Machine" is a mathematical model used to describe the behavior of a system. It consists of a set of states, transitions and actions. A state represents a specific behavior or condition of a system, while a transition defines movement from one state to another. Actions associated with states or transitions represent the logic to be performed upon entering, exiting, or staying in a state [9]. Figure 3 shows the sequence of launching a software application (in particular, a game). GameBootstrapper Game GameStateMachine - Loading screen - SceneLoader ... BootstrapState LoadLevelState LoadProgressState - Loading of a game level - Loading of initial level - Creating and loading game - Registration of services resources - Initialization of needed dependencies Figure 3. Sequence of Actions to Launch an Application. The first step in implementing the State Machine is to define the states. Each state will be represented by a separate script (logic) that inherits from the IState interface, which declares 2 main methods: entering a certain state and exiting it. The next step is to create a GameStateMachine class to manage state transitions and perform appropriate actions. The GameStateMachine class can have different states (for example: `BootstrapState`, `LoadLevelState`, `LoadProgressState`, `GameLoopState`), which can be treated as objects in a category. The transition between states through the Enter() and ChangeState() methods can be analogous to morphisms that transform one object (state) into another. One of the key aspects of category theory is the composition of morphisms, which in this case can be represented as a series of transitions between states. For example, going from `BootstrapState` to `LoadLevelState` and then to `GameLoopState`. The composition of these transitions (morphisms) creates a "path" through different states of the game. In category theory, functors are mappings between categories. One can consider the interaction between different components of the system (for example, between `GameStateMachine` and specific states) as a functor-like relationship, where the behavior of one component determines the behavior of another. A system of states can be considered as a category where states are objects and transitions between them are morphisms. Within a larger system (for example, the entire game), such a state system can be considered a subcategory. In category theory, each object has an identical morphism that reflects the object in itself. This can be implemented as the ability of a state to remain unchanged if there is no transition to another state. Consider the following formula (1), which describes the transition from BootstrapState to GameLoopState via LoadLevelState: 𝐿𝐿𝑆 = 𝐵𝑆 − −> 𝐿𝑃𝑆 − −> 𝐺𝐿𝑆 , (1) This equation can be interpreted as a composition of two morphisms: 1. BootstrapState->LoadProgressState. This morphism corresponds to the transition from BootstrapState to LoadProgressState. 2. LoadProgressState->GameLoopState This morphism corresponds to the transition from LoadProgressState to GameLoopState. The composition of these two morphisms gives the BootstrapState -> GameLoopState morphism, which corresponds to a direct transition from BootstrapState to GameLoopState. This example demonstrates how category theory can be used to describe and understand transitions between states in a system of game states. This mathematical model is a simplified representation of the system of game states. A real system can be much more complex, with more states, transitions, and interactions. Category theory provides a powerful toolkit for modeling and analyzing systems of states, but understanding and applying its concepts requires some knowledge of abstract mathematics. 6. Architecting Game Applications with Category Theory Designing the structure of a service-oriented application (SOA, Service-Oriented Architecture) in Unity requires an understanding of both the basic principles of SOA and the specifics of Unity as a game engine. The main idea of SOA is to create modular, independent services that can be easily replaced, updated, or modified without affecting other parts of the system [10]. SOA defines a way to make software components reusable and interoperable through service interfaces. Services use common interface standards and an architectural pattern so that they can be quickly integrated into new applications. This relieves the task of the application developer who previously redesigned or duplicated existing functionality or had to know how to connect or ensure compatibility with existing functionality. Each service in an SOA contains the code and data needed to perform a complete, discrete business function (such as checking a customer's creditworthiness, calculating a monthly loan payment, or processing a mortgage application). Service interfaces provide free communication, that is, they can be called almost without knowing how the service under them is implemented, which reduces the dependency between applications. Below are the key considerations for SOA design in Unity: • Definition of Services: Functional elements of the application can be separated as independent services. These can be, for example, the game save system, sound management, network interactions, advertising management, etc. • Service Interfaces: It is necessary to clearly define the interfaces for each service. This will allow replacing service implementations without the need to change the code that uses these services. • Dependency Checking and Dependency Injection: Using the Dependency Injection pattern will help manage dependencies between different services and components. This can be implemented through constructors, setters, or through special frameworks (Zenject [15], VContainer [16]). • Projecting to the Service Manager: To manage services, it is necessary to create a central service manager (Service Locator), which will be responsible for initialization, storage, and access to various services in the application. • Design of Modular and Flexible Architecture: Each service must be designed so that it is self- sufficient and can function independently of other parts of the system. This will provide high flexibility and simplify testing and project development. • Deployment and Updating of Services: Methods of deployment and updating of individual services should be able to update services without stopping the entire system. • Testing: Each service is designed taking into account the possibility of its testing. Automated testing is a key element in maintaining high code quality and system stability. • In Unity, SOA can be implemented both with the help of built-in tools (for example, through MonoBehaviour components) and with the help of external libraries or frameworks. The main thing is to keep the focus on clearly defining the roles and responsibilities of each service, as well as on the flexibility and extensibility of the architecture. Additional Considerations for SOA Design in Unity: • Performance Optimization: It’s crucial to consider performance optimization when designing SOA in Unity. Profiling tools like Unity Profiler can help identify bottlenecks and ensure efficient memory and resource management. • Security: Secure communication between services, especially for network interactions, is essential. Implementing proper authentication and authorization mechanisms will help protect sensitive data. • Real-World Examples: Practical examples of SOA in Unity, such as case studies of successful implementations, can provide valuable insights and guidance. For instance, discussing a game that effectively uses SOA principles can illustrate the benefits and challenges faced during development. • Cloud Integration: Leveraging cloud services like AWS or Azure can enhance the scalability and flexibility of Unity applications. Cloud services can be used for hosting, data storage, and processing, enabling seamless scaling of services. • Service Versioning: Maintaining backward compatibility and managing updates through service versioning is vital to ensure that new updates do not disrupt existing functionality. • By incorporating these additional considerations, you can create a more comprehensive and robust guide for designing service-oriented applications in Unity. 7. Creating test app based on recommended approach First of all, we need to create two scenes: InitialScene – where we will manage the loading of the app, resolving dependencies, taking saves, and other preparing actions; MainScene (it can be level, menu, lobby scene whatever that can be after loading scene). The next step is to set up the DI container. In our case for Unity, we need to import the Zenject package. After importing we can set up Project Context, and Installers for every scene. This allows as then bind needed components and inject dependencies where we need them. Also, we need to add a SceneContext component for every scene and create and assign installers to this context. Inside these installers we can bind the Service locator to have access from any place, only need to define it with the [Inject] attribute. Based on the previous explanations about categories Scenes are also objects and switching between them is a morphism. Switching contains a few steps (states) that we manage by StateMachine. That is why the state itself needs to contain two main methods (Fig. 4) Enter() and Exit() something can happen when we go to the next state and if we exit we first need to clear memory, unsubscribe, and dispose. Sometimes we need additionally to have the ability to loop or update something inside the script for these purposes we can use other interfaces to extend our functionality. Figure 4. UML Class Diagram to represent States. We defined states [17] and separated needed logic for loading and play sessions (Fig. 5). In the Bootstrap state we register all our services. Then go to the next scene through LoadLevelState. Usually, we have some stored data on the client or server, for this purpose, we can use LoadProgressState and load if exists or create new data if needed. To store data locally or on the server we can create a SaveData service and register it in the BootstrapState. Then every needed logic we as a service and use it from any place. Figure 5. Diagram of applications’ states. 8. Conclusions In the course of the study, the key provisions of the theory of categories were analyzed, which became the theoretical basis for the development of the application design methodology. It was proposed to interpret the life cycle of the application as a set of states of the program, which go from one to another with the help of the "State Machine". The composition of these states can be represented as a composition of morphisms, a mapping of one state into another, which follows from the theory of categories. The described interpretation enables a controlled scaling of the project architecture and improves the understanding of the execution of processes and logic in the application. New logic we can represent as a service and need only register it to use somewhere. This type of architecture allows us in a comfortable and faster way extend or change our logic, only by replacing the service. The next step for improvement will be to manage the realization of the services interface for example by Strategy pattern. The proposed technique is a theoretical foundation for the development of flexible architectures of multimedia applications. It takes into account the specifics of component-oriented and service- oriented architectural styles, as well as the peculiarities of the functioning of game engines. References [1] Mavrevski, R., Traykov, M. and Trenchev, I., 2019. Finding the shortest path in a graph and its visualization using C# and WPF. International Journal of Electrical and Computer Engineering (IJECE), 10(2), pp.2054-2059. DOI: 10.11591/ijece.v10i2.pp2054-2059. [2] Litvin, A., Velychko, V. and Kaverinsky, V., 2020. Method of information obtaining from ontology on the basis of a natural language phrase analysis, in: CEUR Workshop Proceedings, CEUR-WS, Kyiv, Ukraine, 2020: pp. 323–330. [3] Kulibaba, S., Popereshnyak, S., Shcheblanin, Y., Kurchenko, O., Mazur, N., 2022. Advanced сommunication model with the voice сontrol and the increased security level. In: Cybersecurity Providing in Information and Telecommunication Systems, October 13, 2022, (CPITS-2022) Kyiv, Ukraine. CEUR Workshop Proceedings, 3288, pp.64-72. [4] IBM, no date. What is service-oriented architecture (SOA)?. Available from: https://www.ibm.com/topics/soa [Viewed 3 April 2024]. [5] Popereshnyak, S., Yurchuk, I., 2021. Social Networks: Analysis, Algorithms and Their Implementation. In: 5th International Conference on Computational Linguistics and Intelligent Systems, April 22–23, 2021, Kharkiv, Ukraine. CEUR Workshop Proceedings, 2870, pp.811-821. [6] 2023. Mobile game development using Unity engine. Methodologies and intelligent systems for technology enhanced learning. In: Workshops - 13th International Conference, pp.129- 138. Available from: https://www.researchgate.net/publication/373475694_Mobile_Game_Development_Using_U nity_Engine [Accessed 2 April 2024]. [7] Kaverinsky, V., & Malakhov, K. 2023. Natural Language-Driven Dialogue Systems for Support in Physical Medicine and Rehabilitation. South African Computer Journal, 35(2). https://doi.org/10.18489/sacj.v35i2.17444 [8] Chebanyuk, O., 2023. Approach to reuse of Unity game scenes. In: 2023 International Conference on Advanced Enterprise Information System (AEIS), London, United Kingdom, 2023. pp.11-15. ISBN: 979-8-3503-5926-8. DOI: 10.1109/AEIS61544.2023.00009. [9] Kolev, M., Trenchev, I., Traykov, M., Mavrevski, R. and Ivanov, I., 2023. The impact of virtual and augmented reality on the development of motor skills and coordination in children with special educational needs. In: Zlateva, T. and Tuparov, G. (eds) 19th EAI International Conference on Computer Science and Education in Computer Science, CSECS 2023, Boston, United States, June 28-29, 2023. Lecture Notes of the Institute for Computer Sciences, Social-Informatics and Telecommunications Engineering (LNICST), vol. 514. Springer Nature Switzerland, pp.1-13. [10] Kishor, K., Rani, R. and Rai, A.K., 2023. 3D application development using Unity real-time platform. Proceedings of Fourth Doctoral Symposium on Computational Intelligence, pp.665-675. [11] Mosler, P. et al., 2023. Using the game engine Unity efficiently in teaching: Development of a fully-automated webserver-based build pipeline. In: eCAADe 2023: Digital Design Reconsidered, Graz, Austria, 20-22 September 2023. Available from: https://doi.org/10.52842/conf.ecaade.2023.2.883 [Accessed 5 April 2024]. [12] A. Litvin, V. Velychko, and V. Kaverinsky. A New Approach to Automatic Ontology Generation from the Natural Language Texts with Complex Inflection Structures in the Dialogue Systems Development in: CEUR Workshop Proceedings, CEUR-WS, Kyiv, Ukraine, 2022: pp. 172-185 [13] Boiarskyi, O., Popereshnyak, S., 2022. Automated System and Domain-Specific Language for Medical Data Collection and Processing. In: Babichev, S., Lytvynenko, V. (eds) Lecture Notes in Computational Intelligence and Decision Making. ISDMCI 2021. Lecture Notes on Data Engineering and Communications Technologies, vol 77. Springer, Cham. https://doi.org/10.1007/978-3-030-82014-5_25 [14] Uzayr, S. bin, 2022. Mastering Unity: A Beginner's Guide (Mastering Computer Science). 1st ed. CRC Press. [15] GitHub, no date. GitHub - modesttree/Zenject: Dependency injection framework for Unity3D. Available from: https://github.com/modesttree/Zenject [Accessed 3 April 2024]. [16] VContainer, no date. About | VContainer. Available from: https://vcontainer.hadashikick.jp/ [Accessed 2 April 2024]. [17] ServiceLocatorArchitecturePresentation, no date. Available from: https://youtu.be/nGHrLzqlVWg [Accessed 3 April 2024].