Code Monkey home page Code Monkey logo

lp2_4xgame_p1_2022's Introduction

4X GAME - PHASE 1

In this phase, 4X Game is a Unity 2021.3 LTS game that allows to generate, manipulate and pick a .map4x file from the Desktop to be loaded and displayed as an interactive game map.

The next phase of this project, which contains moving units and turns, is being developed here.

• CODE ARCHITECTURE • • METADATA •


The Game Map

Game Map

Represented by a grid of squared tiles, each visually composed by a terrain with zero to six different resource types, all of which are clickable, displaying its properties: Terrain, Resources, Coin and Food.

For bigger maps, zooming and panning is also made available, through typical keyboard binds. Furthermore, there are 5 numbered buttons (1-5) at the top, used to display map statistics.

Terrains

Terrains

There are 5 terrain types, all visibly distinct, represented by two color tones only. As the foundation of each game tile, terrains are responsible for establishing its base Coin and Food values.

Resources

Resources

There are 6 resource types, also visibly distinct, but not only from each other. On some cases they will vary depending on the terrain they're on. A terrain can have up to 6 resources, each having their own Coin and Food values, which stack.

Below are the pre-defined Coin and Food values for each resource:

Water Resources

And here's a practical example of a game tile with 4 resources:

Inspector


Code Architecture

The game is structured around 5 Main Sections, managed by the Controller.

• PRE-START • • MAP BROWSER • • MAP DISPLAY • • INSPECTOR • • ANALYTICS •


Controller

Manages the game, handling its GameStates and Player Input. It starts by setting the CurrentState to Pre Start, which in turn makes the PanelsUserInterface update it's display, sending it the respective UIStates. These are fairly similar to GameStates, but more specific. For example, the Inspector and Analytics UI states, which are both Pause game states, have different internal behaviours.

The CurrentState is updated following the Observer pattern, through subscribed events, meaning other classes don't need a Controller reference.

User Interface

The game's PanelsUserInterface, which implements the generic IUserInterface (in case we ever want to use some other UI format - Interface Segregation Principle) focuses on enabling and disabling single responsibility panels, visually reflecting the current game section, following the Single Responsibility Principle.

In turn, each panel handles their respective game behaviours, and raises events when the CurrentState needs to be updated. All panels extend an abstract UI Panel, which contains the opening and closing panel behaviours. This follows the Open/Closed Principle, due to the ease of creating new panels without having to modify the base code.


Pre-start

Pre-Start

In the PreStartPanel, an event is raised when the input prompt "Press any key to start" is revealed. This event is subscribed by the Controller, which in turn starts a coroutine that will update the CurrentState to MapBrowser after any key is pressed.

Map Browser

Map Browser

The MapBrowserPanel displays all existing map files inside the Desktop's 'maps4xfiles' folder in a scrollable menu.*

It starts by using the static MapFilesBrowser class to create a MapData instance for each file, and return them. A MapData instance, at this initial stage, contains a string array with all the file lines, a name (that matches the file, without the extension), and the X and Y map dimensions, which are read right away, from the first file line.

For each MapData returned, a MapFileWidget is instantiated, serving as its visual representation, displaying the map's name and dimensions, while referencing it. The displayed name can be edited by the player, which internally also updates the MapData and file's name. Because the file name is editable, cautions have to be taken to not allow for illegal path characters, verified by the static MapFileNameValidator, which replaces illegal characters for _ using Regex, or duplicate names, which is verified by the MapFilesBrowser and adds a _N to the duplicate file.

private static readonly Regex ILLEGAL_CHARS = new Regex("[#%&{}\\<>*?/$!'\":@+`|= ]");
(...)
p_fileName = ILLEGAL_CHARS.Replace(p_fileName, "_");

After each MapFileWidget is instantiated, a MapFileGeneratorWidget is instantiated at the end of the list, allowing direct map files creation, using Nuno Fachada's Map Generator.

Regarding the Map Generator's code, we've made 2 small adjustments to the version we have implemented:

  • Increased the chance of generating resources from 0.3 to 0.5, to generate more diverse maps.
  • Fixed a small bug that prevented very small maps (with x * y > 10) from being generated and caused small maps to only have one or two terrains.
int totalTiles = rows * cols;

int numCenterPoints = (totalTiles > 50)
   ? (int)(totalTiles * centerPointsDensity) 
   : (int)(totalTiles * centerPointsDensity * (100/totalTiles));

When the Load Button is pressed, an event is raised with the selected map (yellow outline), causing the Controller to change its CurrentState to LoadMap.

* The scrollable menu was originally using the Unity's UI Element Scroll Rect component, however due to a mouse scroll wheel bug, it's now using a custom UpgradedScrollRect extension.

Load Map

Before being ready to display, the MapData has to convert its array of lines into an abstract GameTile list, a class that represents a tile's terrain, which may or not have an abstract Resource list.

Using abstract game tiles and resources follows the Dependency Inversion and Open/Closed principles, allowing new game tiles or resources to be easily added without having to modify anything.

The conversion is done by iterating all lines, starting at the second (the first line held the map dimensions, which have already been handled, when MapData was instantiated). In each line, it starts by looking for a #, by trying to get its index. If its greater than 0, then that line has a comment that needs to be ignored, using a substring.

m_commentIndex = m_line.IndexOf("#");
if (m_commentIndex >= 0) m_line = m_line.Substring(0, m_commentIndex);

The line is then split into an array of strings, each representing a keyword. The first will always be a Terrain, so it's compared with the Terrain names the game has and instantiates a GameTile accordingly, adding it to this MapData's GameTile list.

case "desert":
    GameTiles.Insert(i - 1, new DesertTile()); 
    break;

If there are any other words in the string array, each represents a Resource to add to the GameTile we just instantiated. Again, each keyword is compared with the Resource names in the game, and added accordingly.

if (m_lineStrings.Length > 0)
    for (int s = 1; s < m_lineStrings.Length; s++)
        switch (m_lineStrings[s])
        {
            case "plants":
                GameTiles[i - 1].AddResource(new PlantsResource());
                break;

            (...)

Map Display

Map Display

Now that MapData has a GameTile list and is ready to be loaded, the Controller sends it to MapDisplay, responsible for generating the map.

The map is generated using the Grid Layout and Content Size Fitter components. The only adjustments needed is setting the Grid Layout's cell size and the column count constraint, both calculated with the map dimensions.

m_newCellSize.y = MAX_Y_SIZE / p_map.Dimensions_Y;
m_newCellSize.x = MAX_X_SIZE / p_map.Dimensions_X;

// Sets both the X and Y to the lowest value out of the 2, making a square.
if (m_newCellSize.y < m_newCellSize.x) m_newCellSize.x = m_newCellSize.y;
else m_newCellSize.y = m_newCellSize.x;

_cellSize = m_newCellSize.x;

// Constraints the grid layout group to a max of X columns.
_gridLayout.constraintCount = p_map.Dimensions_X;

With the grid prepared, MapDisplay then iterates every GameTile in the MapData's list and instantiates a MapCell prefab for each. A MapCell represents a game tile visually, only holding its terrain and resources sprites.

Once all are instantiated, MapDisplay then raises an event that makes the Controller tell the PanelsUserInterface that it can now enable the MapDisplayPanel, rendering the map visible, and disables the Grid Layout and Content Size Fitter components, boosting performance by reducing automatic component calls.

In this state, the map can be moved and zoomed in/out, using the key binds shown on the bottom of the screen. The player's input is handled by the Controller, who then passes the directional info to the MapDisplay that tries to move the map using its Rect Transform's pivot.

Using the pivot to move is more performant heavy, but allows for centered zooming in and out when the Rect Transform is scaled.

Each MapCell can also be hovered and clicked by the mouse through Unity's Event Trigger component, updating its base sprite to look hovered and raising two events when clicked. One triggers the Controller to display the InspectorPanel, while the other sends out the data needed to inspect.

Inspector

Inspector

The InspectorPanel is responsible for displaying the clicked MapCell's properties. It does so by syncing its name, coin and food values, plus the terrain and resources sprites with the clicked cell. Furthermore, it enables text components accordingly to the shown resources, to add extra written info. This written info is equal for every tile, since the Coin and Food values of resources and terrains are constant. The only values that vary are the GameTile's totals.

Merely a "show" type of panel, when the user presses escape or clicks away, the Controller updates its CurrentState to Gameplay.

Analytics

Analytics

The AnalyticsPanel is triggered by clicking any of the numbered buttons on the top of the screen. When clicked, an event on the button itself is raised, containing the button index, which makes the Controller display this panel, that displays info according to the index.

The panel itself, originally wasn't supposed to show anything, but we've decided to implement the suggested LINQ template functionalities. As such, pressing each of the buttons will display:

1. Number of tiles without resources.
2. Coin average in mountain tiles.
3. List of existing terrains, alphabetically.
4. The terrain, resources and coordinates of the tile with the least Coin value.
5. Number of unique tiles.

Also a "show" type of panel, when the user presses escape or clicks away, the Controller updates its CurrentState to Gameplay.


UML Diagram

UML

References

Metadata

Afonso Lage (a21901381) André Santos (a21901767)
GameTile & Resource + subclasses Controller with GameStates & UIStates
Visual Map Grid Generation Files Browser
Map Cells Map Control (Zoom & Pan)
Linq Operations UIPanel Classes + Visual UI
XML Documentation (50%) XML Documentation (50%)
README (50%) README (50%)

Game created as 1st Project for Programming Languages II, 2022/23.
Professor: Nuno Fachada.
Bachelor in Videogames at Lusófona University.

• BACK TO TOP •

lp2_4xgame_p1_2022's People

Contributors

andrepucas avatar afonsolage-boop avatar

Stargazers

 avatar

Watchers

Nuno Fachada avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.