Welcome, Guest. Please login or register.

Login with username, password and session length

 
Advanced search

1411275 Posts in 69323 Topics- by 58380 Members - Latest Member: bob1029

March 28, 2024, 05:07:16 AM

Need hosting? Check out Digital Ocean
(more details in this thread)
TIGSource ForumsCommunityDevLogsOmega Gateway - 4x Sci Fi game inspired in Master of Orion 2 and Star Trek: BOFT
Pages: [1]
Print
Author Topic: Omega Gateway - 4x Sci Fi game inspired in Master of Orion 2 and Star Trek: BOFT  (Read 753 times)
Kanyenke
Level 0
*

Emiliano Dev


View Profile WWW
« on: October 02, 2021, 09:25:26 AM »

Ep 1 - Introduction and Grid Generation

Hello fellow galaxy seekers! My name is Emiliano, I’m a 34 year old developer originally from La Pampa, Argentina; but currently living in Amsterdam, Netherlands.

I have been doing some interesting yet unfinished game development projects in my free time since quite some time already, but this time I’d like to share my progress with the world with the idea of keeping myself motivated and in the meantime getting some feedback.

So, what are we trying to do next? I really like 4x games, and particularly I love sci-fi 4x games, like Stellaris, Sword of the Stars, Master of Orion 2, and one of my favorites and the most important inspiration for our prototype, Star Trek: Birth of the Federation.

That being said, we need to keep the scope in check; because I’m a solo dev, and although these amazing games have a lot of amazing mechanics, we have to start small and grow organically.

First I created an infinite canvas in Miro where I can brainstorm, capture and understand the features that I like the most about these 4x games, and also add my own mechanics that I think would be cool.



After that I wrote down a very basic Design Document where I set the scope for two versions: a prototype version with an extremely small list of mechanics that COULD show if the idea is fun or not; and a probable ALPHA version that could capture the idea of a full game a little better.

I’m pretty sure we will be revising and rechecking these versions anyways, but the features list is there.





We need a name for our prototype: Given that we want to reach at least the Alpha version, let’s make sure we reach that point by calling the game: Omega Gateway. We can change it later if it doesn't work anymore.

By the way, we are going to work in Unity, not for any particular reason other than its the engine how to use the most and I'm very familiar already with C#.

So, let's get to the action, shall we? First of all, I’m creating a new Unity project. Given that most of the elements that we want to show in the game are basically UI components, a 2D game template would be the best here in my opinion. Also, this doesn’t prevent you from doing 3d stuff later, so we are going 2d.

Before anything, just to set the mood, I'll set a galactic background. For the time being, I’m going to download something from opengameart.org, just edit it a little bit for my convenience on Paint.net, and there you go.



So, the first idea now is to generate a grid in Unity that although may look horrible, it has its own identifiable coordinate. Just like in BOTF, we are going to label all the columns with a letter starting from capital A, and numbers for the rows.

I can think of two data structures for a composition like this: First, an array of arrays: So every element of the columns array will have another array which represents the rows. You would be able to access the “Sector” in a native way by knowing the two integers that correspond with the coordinates.

The other option, and the one I'm more inclined to, is to have a Dictionary (something like a hashmap for Unity’s C#) with the coordinates as a key and the sector as a value. It seems easier to create a proper interface using this structure, so I'm going this way.

I’ll create a GalaxyCoordinate class and a GalaxySector class, GalaxyCoordinate will just have the column letter as a Char and the row number as an integer. My plan is to use this object as the key of my dictionary. I really don’t know if it is inefficient … So if you know this, please let me know in the comments.
  
I'll also create a GalaxyManager class that will be in charge of… managing the galaxy - mostly map related things. This manager will have a field with a size that you can configure from the editor. We are also adding a seed integer that we could use to generate the same list of random values.

I am going to create a very simple tile border. Going to make it on paint.net, white with some transparency so we can play with the color on Unity if we want.
Now In Unity, I'm creating a new object with this tile image as a sprite renderer component. I'm also adding the GalaxySector class as another component. We save this sector grid as a prefab.

So, the very basic idea is to iterate through the size, two times (one for columns and one for rows), and in each element we create a new GalaxyCoordinate object.

You may notice that we start our iteration for rows in 65: this is because if we cast the integer 65 to a char it's a capital letter “A”, so it's basically the same as using numbers but showing it as a letter.
 



For instantiating the grid on screen, we can make each grid size 1, just to make spacing really simple.

Oops, I forgot that positive numbers on the Y axis go up, but that was easy to fix.
I also gave it a name to see it is being identified correctly, which it is.

As you can see, now we generate a neat space grid, and every GalaxySector has its correct coordinate.



For the next post, we are going to check what’s the best criteria to add Systems into those sectors, using an algorithm to determine whether it should or should not have one, and then draw a star there.

See you around, and cheers friends!

Oh, you can see the video version of this devlog over here:



Also, You can reach me on Twitter! https://twitter.com/EmilianoCenizo
« Last Edit: October 16, 2021, 09:43:10 AM by Kanyenke » Logged
Kanyenke
Level 0
*

Emiliano Dev


View Profile WWW
« Reply #1 on: October 09, 2021, 06:52:24 AM »

Procedurally distributing a bunch of stars

Hey there fellow grid lovers! You stumbled upon the devlog for Omega Gateway, an upcoming sci-fi 4x game based on old classics like Master of Orion 2 and Star Trek Birth of the Federation.

Last time, we spent some time putting together a feature list that we want to have for our prototype, to check if it is playable.

After that, we worked a little bit on how to draw a simple 2d grid that has some well set coordinates, visible on the code and the editor.

We also have the interfaces on our code to work on the systems and to draw a star in every sector that has one, so let's get to that.

There are many many distributions we could use and that are popular in the genre: spheric, spiral, clusters, even some with arms that go from the center to the sides like tendrils.

Some very very interesting information and algorithms can be found in Martin Devans' blog: https://martindevans.me/game-development/2016/01/14/Procedural-Generation-For-Dummies-Galaxies/

First, let’s check our distribution algorithm. FYI, I’m pretty sure there are better ways to do this, so don’t @ me. Actually, do @ me and please let me know in the comments.

We are going to have Unity’s native Random module to generate a number of values, consuming the seed we set from the Editor. For now, don’t set a seed if the seed is zero.

According to Unity’s documentation, the pseudorandomness the module generates should be entropic enough so we could get a pretty normal distribution every time.

So, if we just iterate through every sector and ask for a random number between -1 and 1, and then we ask if the value is greater or equal than zero, we should have a very consistent close to 50% of systems in our grid.

If we chose a minimum threshold lower than -1, we will have even less systems. We can play with that.



Let’s try this theory by drawing a star in every sector in the map with the same priority.

Again I’m drawing a very basic png with some transparency to represent the presence of a star. From Unity we can play with things like the color if we want. We will save it as a SystemStar prefab.

We will also create our GalaxySystem class. A few worth mentioning attributes are:
* The System name: I think we will have a proper list with cool galactic names that we can set randomly or with some criteria, but for now i'm just creating a name by taking random chars from a list using this quick auxiliary method I found online.
* The star color: I'm going to default to a horrible yellow just for testing, but we could use this to make the map look amazing and more astronomically correct.
* GalaxySector: a reference to the sector that holds the system.
Talking about the GalaxySector: We are also adding a new GalaxySystem attribute to it: If it is null then that means that it’s a systemless sector.



Finally we add our new GalaxySystem class as a component into our StarSystem prefab.

Now, to the proper instantiation of systems and stars:  We will add a private field for the value that we will use as the minimum threshold for defining the density of systems in our galaxy, in our first test -1. Then, while we are iterating through the columns and rows, we ask if the random value is greater or equals to zero. If that’s the case, we got a system, baby.



We define the build system function and we take the sector as a parameter. In that function we instantiate the new StarSystem (with the prefab we previously added from the editor), and use the override method that includes the parent of the transport, which is going to be the sector itself so each corresponding sector will have a system as a child. Finally we give the reference of the sector to the system and vice versa, just in case.

Back in the GalaxySystem class we will be adding a few things on the OnEnable override method, that will be the first thing that will be run when this object is created.
Pretty simple though: We will get a random gibberish name for the system with the algorithm I mentioned earlier and we will set it also as the object name for clarity in the Editor.
We will also get the Sprite and assign the ugly yellow we mentioned before, and we will also set the local position of the object to zero, so it's on the center of the sector.
Just like the color, we might want to play around this value later so we give the galaxy a bit more diversity.
 
Now if we hit play… voilá! About 50% of the map is covered with a System that has a weird name and a hideous yellow.



So, let's go to the Galaxy Manager so we can play around with the minimum threshold number: As we said, if we set a smaller number than -1, we will have even less systems.
So what i'm going to do is to define a System Galaxy Enumerator with just 3 values: HIGH, MEDIUM and LOW.
And we will set a field in the manager of this class that can be changed from the editor.
Finally, we will set a few thresholds depending on the option you chose: High would be -2 - about 33% of systems in the map. Medium will be -5 - about 20%, and low will be -8 - something around 10%.

And indeed, if we change these values we are generating different amounts of systems in different sectors. Pretty cool, huh?



But... we still haven't finished with the Galaxy generation process: in fact, this distribution is too simple, we can’t ensure some basics like making the starting area of each player as fair and balanced as possible. But that’s something we are going to do in future episodes.

For the next episode though, I think we are going to work on the first steps in the UI: A logo to represent the presence of a ship or fleet in a sector, and allow it to “exist” on a specific sector as well to move it around the galaxy. We might as well throw in the “next turn” button and turn counter to check if it moves.

Again, thank you for watching! And if you like what we are doing here and would like to know more info about Omega Gateway, don't forget to subscribe to the channel!

Cheers, people!
Logged
Kanyenke
Level 0
*

Emiliano Dev


View Profile WWW
« Reply #2 on: October 16, 2021, 09:42:08 AM »

UI Drawing, debug mode, C# goodness and SHIPS INTO SPAAAACE
[/size]

Helloooooo fellow star distributors! We are here yet again, working on the development of Omega Gateway, upcoming 4c sci fi game of the likes of Master of Orion 2 and Star Trek, Birth of the Federation.

Last time we worked on distributing systems on the grid and giving it a name. This time, we are going to focus on the UI. We will also build the base code for supporting different players in the game, and also represent the presence of a ship in the grid.

First, a couple of definitions: While we really want to have 3d models of the ships at some point - like on the production list or in battle - in the zoomed out map of the galaxy we will only show an icon representing the presence of a ship or ships of a particular player.

This works for most of the current 4x games out there right now, and it will be a good fit for our game as well.

First, we are going to work a little bit on the UI. The plan is to have a menu on the left with quick access to the main meta-systems the game will have.



We will draw a general information bar in the top that we will use for instance for money; and the turn information on the top right. I felt that showing the current player icon on the top left could also look cool and set the mood.

Finally, I would like to have two extra information screens for the galaxy map: one that shows information on the selected item, for example sector information about the systems it has, or the number and types of ships on it; and then a smaller box where you can see quick info when hovering over a specific thing.

We also need to define a player entity: We are just going to have a very simple class that has some very simple attributes on it: a playerName, a playerId - which represents the number of player in the game, the logo and the player color defaulted to a sprite I already did on Paint.net and to a nice purplish.



I haven’t decided yet if we are going to have fixed races to choose from or we can edit them before we play, but in any case it's a good idea to define players customizable like this.

Then I decided that it would be very convenient to have some of our Manager classes as Singletons, so we can access them through the applications.

I am not a big fan of this pattern, but it's really useful when implemented with classes that will only have a single instance on the whole application, like for instance our new GameManager.

GameManager will be in charge of dealing with settings related to the run itself, like the number of players, passing the turn, etcetera. We will also define the main Player, and add a method to get the current player at any moment.



Our UIManager will be in charge of the interaction between our UI and the code, like for instance rendering info somewhere as text.



You all know the GalaxyManager by now. For the time being it doesnt need to be a singleton, because I don't need everyone to have access to it, but we can let the GameManager have the instance of it as an attribute to call it from there.

Finally, I’ll add a ShipManager, and for the time being will only be in charge of holding all the ships prefabs we are going to have, so you can access all of them from wherever you want - for example if you need to instantiate one, like we are going to do in a minute.



Talking about Ships, we are going to have an abstract class called Ship that will hold the common attributes and methods for all the ships we are going to have.

For the time being we are adding: the owner (which is a player), a reference to the current sector that it is in, a speed value, a name, a class, some hp and shields, and a GalacticRange, which represents the distance the ship can go from a owned point, a mechanic I liked form Birth of the Federation. This Galactic Range is a very simple enum.



With the abstract ship in place, we create an extremely simple Destroyer class that inherits from it, and for the time being we just leave it empty.

But now we can create a new prefab out of an empty gameobject and attach the destroyer as a component. This is where we are going to add a 3d model when we build one, but for the time being it’s just “invisible”. We will set some values for it as well and save it.

All right! Now we build the UI as we planned. Again, we are going for a prototype here, so I'll leave most of the default Unity look for the time being.

That being said, it's not looking bad at all. The main challenge here was to use Unity’s default layout components to make the things stay where we want them to stay.



Of course, a lot of things need to be adjusted; but for the time being this will work. Also, I added a “debug menu” object with a button, a dropbox and a text field. From here we are going to create ships to test.

Before that we will create a small controller for this DebugMenu: It will have as serialized fields both the text of the input text, that we will use to get the coordinate where we want our ship, and the dropbox values, where we will get which type of ship we chose.

We have to tell the dropbox to contain all our available ships, and those will be coming from our ShipManager as we mentioned before.

On the ShipManager we just add a Serialized attribute, availableShips, and its getter. Now on the editor we add the destroyer prefab to it so it has one element at least.

Back on the debug controller: on its start method we tell it to read from the ship manager instance and then we have to do this… weird thing to populate the dropbox. Basically we need to create some option values objects and that’s how you do it. Just… don't question it, it works.



You might be noticing all the red underline coming from the UnityEngine dot UI package. For some reason Visual Studio Code is not finding this particular import, but the compiler in Unity is working fine… so bear with me and ignore it please.

We finally add a new method to do the magic: this will take whatever is on the textbox and try to convert whatever is text into a char; and then the numbers, to try to create a new GalaxyCoordinate.
We will also get the ship chosen from the dropbox, where we have only one now, and call our gamemanager with a new method to spawn a ship.

This method will be a very simple relay between both classes, where we just get which Sector belongs to the coordinates we set on the text and then we add the ship to it.

We will also add a new method on the Galaxy Sector class that finally does the thing: adds the ship to its list and instantiates it with the sector as the parent.

We also modified the Galaxy Sector prefab, so it has a “ship presence icon”: For the time being, it will just pick the player’s icon and color render it on a corner.

We will have to make this check after every turn to see if there are ships present and then do stuff, but we can do that later.



This should be it! We run the game, write down A4 as our sector and then… what’s happening? We would like to see an error if you write a non-existing coordinate, but we are very sure A4 is in there.

After some researching, I finally got the problem: C# checks the value of the dictionary we are using to get coordinates comparing it with an equals: and although the row and column value of both coordinate objects (the one im sending from the debug controller and the one stored in the dictionary) are the same, they are two different objects in the end.



So to fix this problem, I had to write a GalaxyCoordinateComparer that inherits from the Interface that C# uses for comparison: IEqualityComprarer.
On it, I could override the equals method, which is the one used to understand if two objects are the same. So we override it, telling it to consider two objects equal if the String they form is the same. So two different Coordinate objects that have the same row and column, are considered the same now.

We also need to write a GetHashCode method, that should return a different number for every different coordinate, so we just form a number with the rows and columns we have and add a 0 in the middle just in case.
Finally, We modify the GalaxyManager class so when we create the Dictionary of the sectors, it uses a custom made comparator.



And now, as you can see, it works… Kinda: The icon is spawned where we told them, because we have one destroyer on it; but the rows and columns are reversed! This should be A4, not this.

In any case, this is significant progress for now. In the next chapter, we are going to actually send information to the ugly UI we built, prepare a cool process for passing turns, and let’s try to move the ship if we can.



If you want to check the video version of this devlog, you can over my YouTube channel: https://www.youtube.com/channel/UCKSRKwxChnUw_uYeSOfMa7g

Cheers peeps! See you in the next GALACTIC mission.
Logged
Kanyenke
Level 0
*

Emiliano Dev


View Profile WWW
« Reply #3 on: October 24, 2021, 10:11:29 AM »

Ep 4: The art of ship displacement. Setting Delegates and Events in Unity, clicking behavior components and a queue of actions for moving ships.

Helloooooo fellow ship spawners! You are arriving again to the dark and twisted devlog dimension for the development of Omega Gateway: WIP 4x sci fi game made by and for fans of classics like Master of Orion 2 and Star Trek: Birth of the Federation.

On the last log entry we managed to spawn a Ship entity using our newly drawn ugly UI. The existence of that ship is represented by the player’s icon on the sector that it is in.
Today we are going to work on getting that ship moving, but for that we need to set a few things in place first.

First of all, a event system for the turns:
I started the development trying out a Observer pattern structure for handling the turns:
This means that whenever we press the “next turn” button, this object - most likely the GameManager - will be in charge of calling all the subscribers in a list and letting them know through an interface that the turn has passed.




I almost went this way, but then I noticed that Unity has a system in place for this exact behavior: Unity’s Delegates.

It works basically the same, the only difference is that the “notifier” class has to have a delegate object and an event object.
Subscribers that want to know when a turn has changed have to add a method directly to it, and whenever it is called every subscriber will be called with the parameters you set.

For the UIManager for example, this could be the turn number, so we can change in the top right, like this:





With that out of the way, let's build some components to support selecting things and hovering on things.

I was playing around with several ideas over the week to see what’s the best way to handle these two things and keep the code clean.

So after some deliberation, this is the plan: For elements that show info when they are hovered, I’m just adding collider components and an OnMouseEnter and OnMouseExit actions on every class that need one.
I thought about going with an Interface like “iHoverable” but then I realized it didn't make a lot of sense for the limited amount of code we are adding here.
For the time being, when you are hovering one of these classes, you call the respective method and from that one the UI manager to set the info on a new textbox on the bottom of the screen. It's currently looking like this





I really like how it looks.
We can also set a hide method on the OnMouseExit so we clean that info box when we leave the object.

But now, going to selecting stuff: This one is more interesting, for several reasons:

First, selecting things will show more than a small info text; in fact, it should open its own info panel with its own characteristics: for instance, when selecting a System, it should show the planets it has and what features those have.
When selecting a fleet, it should show its composition, available actions and the possibility to move it around the map.

So this time, we ARE going to go for an interface… an ISelectable interface. Everything that can be selected has to implement this.

We are also going to build a component for selecting stuff and to hold the common behaviour of the art of selecting: like identifying if you are left or right clicking.
We will call that SelectableEntity, and the first thing it will try when it starts will be to get the ISelectable component: if it doesn't find it, then it shows an error. So you can't just add this on any object, it needs to implement that interface.

Then, the behaviour when clicking: We will use Unity’s EventSystem for understanding if the cursor is on top of a specific object.

Again, its showing a red underline but I promise its working.





So, If we left click on an ISelectable - so its Input dot GetMouseButtonDown ZERO - we are going to call one of the interface methods: OnSelect.

We will also do the same on the right click: mouse button down 1; and we will call the other method of the interface, OnAction.

So, any entity that wants to support being selected and being right clicked has to implement the interface ISelectable; and thus, has to override the methods OnSelect and OnAction.



In the future, as we mentioned, we will add some specific UI that will be shown for specific OnSelect actions, but let's work on moving the ships first.

Before that, a small change on how we define ships: In a past episode we created an abstract Ship Class and we were going to extend ship types by inheriting that class and adding particular behaviour.
However, we are changing the approach: Ship will be a concrete class, and every ship variation will be a different prefab variariant with different values and different class components if needed. This will allow us to take full advantage of Unity’s instantiation features and setting different models way more easily.

So, let's add the onselect behaviour to the ships, and for that we will add the new ISelectable interface to our ShipPresenceIndicator. We must implement the OnSelect and OnAction methods.

We can leave OnAction empty for now; but for onselect, we will create a small field on the game manager to support the idea of a “selected Iselectable”.

This will allow us to get what's selected from anywhere on the code, given that our game manager is a singleton.





So on our Onselect on the Ship indicator we just set itself as the new selected item.

Now, although the behaviour starts from the ship's presence, the action happens when you hover over a Sector:
So, on the OnHover method in the Galaxy Sector class (called by the OnMouseEnter event we defined earlier) we will call the game manager to get the selected ISelectable.
Then, we will use the IS keyword to understand if that ISelected is in fact a ShipIndicator. If it is, we will call a new method to handle a selected fleet.

To test if this works, we will draw a small circle in the middle of the sector and show it only if we are hovering over the sector, and hide it if we are not or if we don't have a ship indicator selected. As you can see, this works wonderfully.



Let’s get our ship to move, shall we? I have been doing some thinking about what’s the best way to execute the order between turns for ships, especially when having a long list of coordinates to follow.
Where should these “parameters” live? The plan will be the following: We will create an abstract ShipAction class that will hold a Dictionary with actions as keys, and an object as a value.

These will be the parameters that our action will read. We will also add an abstract doAction method that the concrete action type inheriting from ShipAction will have to implement with its respective parameters.
Then, we will create the MoveAction class that inherits from ShipAction: The constructor will just take the ship and the parameters, but the doAction method that we are overriding will do the magic.





Fair warning though: even when using the most of polymorphism with the object values like this,  there are more elegant ways than passing on a dictionary with simple strings as keys, but for the time being this will test if this structure holds the test of time… and debugging.

But because we are using simple strings as keys and simple objects as values, we need to verify if we have everything we need to move the ship, so we check if among the keys of the parameters we have a “location” string, and if that key is a GalacticCoordinate class object.
This structural validation is all we do here, but the business logic (like for instance, if the coordinate is a valid spot for moving into with this ship) will be done on the ship logic itself.
If everything is valid, we call the move function of the ship with the coordinate as a parameter. Of course, we need to define it first.

It will be pretty simple though: Given that most of the heavy lifting regarding how we render the ships are handled already on the sector and the shipindicator class, we can take the ship from one sector and put it into another.
We will define the shipleftSector and shipArrivedToSector methods on the GalaxySector class, but as you can see, they just add or remove the ship, and then they call the function to render the ship presence icon if needed.

We are just missing one thing: how to pass the move action! Remember that the ISelectable interface also has an abstract method for right clicking? Let’s mess with that.
So, same way we did on the OnHover method, on the OnAction method we will check if we have a selected item from the Game Manager and check if it is a ShipIndicator.
We will wrap the code in this small private method that will just take the coordinate of the sector in a list and push it into a method for the indicator class to handle the action.
Why a list, you may be asking? Right now we are moving to one coordinate, but in the future we want to queue several ones, with a pathfinding algorithm. * spoilers *





Before we go to implement the movement though, we forgot to add something to hold the ShipActions we defined before on the ship:
Given that these actions make their effect once when you change turns, we can define a Queue of ShipActions as a new attribute in the Ship class.
Then we will have some simple methods to play with this: A queue action that just adds an action to the queue and a clear actions to clear them.
We must also not forget to subscribe the Ship class  to the Game Manager’s on Next turn event with our own method, that will take the first action from the queue and execute it.

Sooooo, now that we can queue actions for ships, on the ShipIndicator class we can define the handleFleetMovement method. Here is where we should address things like the speed of the ship, but we can do that later.





For now, we will iterate through every coordinate we are sending as a path, and for each one we do an elegant call on a method using ForEach LinQ . We will tell all the ships in the list to clear their action queues, and then to add a new MoveAction.
This move action will have the ship itself as a parameter, and the Dictionary of parameters, defined previously with just the string “location” as a key and the coordinate we are going to as a value.

That should be it! The ship moves from one sector to the other between turns: The ship indicator checks for ships in the sector and renders the player owner's icon if there is one.





There is a clear business logic issue here though: the ships are teleporting to the indicated sector, instead of drawing a path there.

But worry not, my friends. We will implement a pathfinding A star algorithm in the next episode, and this will find the quickest way to reach a sector ignoring all the non “walkable” ones.  

Thanks again for reading!
Don't forget that you can watch the video version of the log right here:





Goodbye my ship captains; stay safe and see you on the next episode!
« Last Edit: October 24, 2021, 10:25:53 AM by Kanyenke » Logged
Pages: [1]
Print
Jump to:  

Theme orange-lt created by panic