Announcement

Collapse
No announcement yet.

Engine/Game Design Patterns

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

    Engine/Game Design Patterns

    I'm interested in discussing which design patterns fit with the existing workflow/patterns in the engine. More specifically, what patterns/design decisions are a best fit for common game related problems. I hope this thread can turn into a resource we can all use to apply to our own projects. To start off, here's an example I'm working to solve at the moment.

    Suppose we have a team-based multiplayer game mode with the following requirements:
    • A team is a group of players
    • There are N different types of teams (Assume a Blueprint to represent each type of team; all teams derived from a base class ATeamBase, which derives from AActor)
    • Each player must belong to a team; Each player derives from base class ACharacter
    • Each team has a persistent unique ID, which is used to retrieve persistent data that describes the team i.e Type, Name, Skin etc
    • When a player joins a game, if their team doesn't already exist in the world (i.e. team mate already in server), it is instantiated into the world through (World()->SpawnActor()) which is visually represented i.e TeamHQ
    • Players must be able to send messages to their team mates
    • Players always spawn at their TeamHQ
    • When all players from the same team leave the game, the team Actor is destroyed


    This presents a few decisions that have to be made:
    • Who is responsible for creating the team and type i.e. Aliens/Marines, Cowboys/Indians etc? They must have knowledge of which Blueprint corresponds to each team type
    • Who is responsible for keeping references to the teams? So we can check if a team already exists, perform a function on a specific team, or iterate over all the teams
    • Who is responsible for deleting the team (When no players are left)?


    There are a few potential solutions to this:
    • Allow the GameMode to be responsible for everything. i.e. Create the team, Add the player to the team, Delete the team when the last player leaves. (Violates single responsibility principle)
    • Create a 'Team Manager' class to handle the creation, store references and delete the team. The Team class then just becomes a container for players in the team. GameMode calls Team Manager functions. (Manager classes are generally considered as a sign of not understanding the problem properly. Most of the functionality can be moved into the Team class itself. Would use IoC solution to DI the manager into classes where it's a dependency. Unsure of implementation in UE4. Need to locate something like C# Autofac)
    • Create static methods/variables in the team class to create/store references and delete teams. (Much like the singleton pattern, this makes Unit Testing difficult and is not loosely coupled)
    • Encapsulate each function into its own class. i.e. Team Factory Class to handle creation etc. (Can lead to fragmented code base for little benefit in some cases)


    I'd be interested to hear how you would design a solution to this common problem as it pops up quite frequently. i.e. Particle System. Who is responsible for creating/deleting, keeping references so the particles can be iterated over.

    #2
    I'm hoping this thread gets more contributors.

    I'm not a fan on the first and last solutions. The first is definitely the wrong place and the last does feel like it is too fragmented.

    Having said that, I've got separate 'storagein' and 'storageout' classes myself It keeps the IO code isolated from the rest of the code instead of all over the place, and helps when reading/writing multiple versions. Overall it's been a plus, although I'm not sure how well it works with unreal compared to other ways of doing things. I know that separation is going to be considered bad by some people since it's the 'wrong' kind, but it's been helpful.

    I would follow KISS and go for the static methods initially and figure on changing it later if needed. The Team Manager class feels to me like it doesn't have enough code for a separate class, at least to begin with. A purist would insist on it as a separate class for unit testing, and I wouldn't argue with them.

    Unreal does have a reflection system, so working up something like Autofac should be possible.
    https://www.unrealengine.com/blog/un...tem-reflection

    Comment


      #3
      I'd like to hear from Epic's engineers on what patterns they would suggest.

      Like all design patterns, there are always caveats to using whichever you end up choosing. In this case, some class needs to be responsible for or at least triggering the creation/deletion of the teams, as well as storing an array to iterate over. All of which steers me toward using a 'manager' class. Conventional wisdom says avoid it, but in this case I can't see a nicer solution. It's loosely coupled and moves the team class more toward being a POD type.

      Comment


        #4
        Have a look at the ShooterGame example.

        In particular:
        ShooterGame\Source\ShooterGame\Classes\Online\ShooterPlayerState.h
        ShooterGame\Source\ShooterGame\Classes\Online\ShooterGame_TeamDeathMatch.h

        (psudocode below, comments are mine)

        Code:
        class AShooterGame_TeamDeathMatch : public AShooterGameMode {
        // number of teams
        // function to pick a team given a AShooterPlayerState
        }
        class AShooterPlayerState : public APlayerState {
        // Scoreboard data: kills, deaths, name
        // team number
        }
        class AShooterCharacter : public ACharacter {
        // super class APawn::PlayerState holds a reference to AShooterPlayerState
        // Life time stuff: health, ammo, motion, functions for actions
        }
        Also try searching the project for "PlayerState".

        I didn't see any class that holds a list of all the players on each team.
        I did see a function which builds a list of players on a given team: AShooterGameState::GetRankedMap().


        Having source code is awsome, but I too would still appreciate further advice/wisdom/notes/suggestions from Epic.

        Establishing "the right way" will be very beneficial to developers joining other teams.
        It would suck to see many projects each doing things their own way, many would likely do something silly.

        Comment


          #5
          In the case of the Shooter example, despite defining a team by ID, it is more or less implicit and it doesn't need to be any more than that. It's only used for assigning colours to the team meshes/discriminating projectile hits. Given there are also only two teams at most for that particular game mode, you can make a lot more assumptions than if there were more. In this case, if you aren't on team x, you have to be on team y. As soon as you add more requirements in the mix as defined above, you do need to store references to them. Building a list of teams, whenever you require access to a specific team is just wasting cycles. This of course depends on the role the team has in the game. In the shooter example TDM, seldom do you need to perform a team function.
          Last edited by ZachGriffin; 04-07-2014, 11:08 AM.

          Comment


            #6
            I would have a pointer on every player that points to a Team object. When you join a team, it points to the corresponding team object for the team that you join.

            Team object simply stores Team stats and a TArray of pointers to all players on the team (to display individual player and full team scoreboard statistics).
            The team pointer on each player is used to check Team in order to properly show players (think mini-maps) and handle things like Friendly Fire.

            It would make sense to me to have one person code all of the Team/Player management systems. There is no difference between a Cowboy/Alien/Indian/Tank at the base level. The difference comes with things like "Cowboys use guns and Indians use Bows" and "Cowboys spawn here". Cowboys/Aliens/Tanks/Indians would all derive from your Player class which would handle all of the common attributes like Kills and Deaths.
            Last edited by alg0801; 04-07-2014, 01:37 PM.

            Comment

            Working...
            X