greenstack / spite-framework Goto Github PK
View Code? Open in Web Editor NEWThe Spite Framework is a C# library meant to simplify designing and implementing turn-based gameplay by providing some boilerplate code.
License: MIT License
The Spite Framework is a C# library meant to simplify designing and implementing turn-based gameplay by providing some boilerplate code.
License: MIT License
Before I deem 0.2.0-alpha complete, I want to make sure that the current codebase actually works for making a game. The battleship example will help serve this purpose.
Right now, Spite can't be directly cloned into Unity projects because the framework uses C# features not available in Unity (namely, certain C# 8.0 features). Since I want Spite to be as compatible with as many engines and frameworks as possible, I'm going to make the target Unity version Unity 2019 since that has support until 2022 (Unity 2018 loses support this year). This means that Spite should only use features available in C# 7.3 (source).
Once Unity 2019 leaves LTS in 2022, we can look into updating available language features to what Unity 2020 supports - in other words, we have Spite supporting the oldest available LTS version of Unity.
That said, I'm open to discussion for this (for example, maybe we target Unity's recommended release, which in this case is Unity 2020. This way, we keep a balance of getting to use newer language features. Maybe that's what we do once Spite gets its first full release.)
Part of the solution for this is making sure that Spite doesn't use a C# version greater than 7.3.
Currently, in 2-sided games, it's not very easy to get the opposing team of another team.
Here's the current method used in SpiteBattleship (BattleshipTeam
's DetermineStanding
implementation):
foreach (var team in context.GetTeamsOpposing(this))
{
if (!team.AreAnyEntitiesAlive())
{
// There's only two teams in battleship, so we can easily determine this.
CurrentStanding = TeamStanding.Victorious;
break;
}
}
Considering the prevalence of two-sided matches, there should be some way to get the singular opposing team. I'm not a fan of having to use a for
loop on it.
Whatever the solution is, it should also support multi-sided games as well.
If a team count isn't supplied to an arena builder, the team count should be inferred from the number of teams added. Otherwise, it should retain the current behavior.
An Alliance Graph is a graph data structure that contains the relationships between teams. A node in the graph is a team, and each edge in the graph is the relationship between the teams. This solution can help solve the problem posited by #5 by providing various helper methods to traverse/search the graph for specific relationships.
Stats are common in turn-based games. Adding basic stats would be very useful.
Stats should be architected to support various numeric types and should eventually support some kind of pipeline pattern. It doesn't need to be exactly like this, but I'm willing to bet there's a lot to learn here. But that's out of the scope for this issue. For now, I just want there to be some kind of serializable container for these stat types.
Though the concept of Sides was removed in 0.2.0-alpha, there is another way in which they can be useful. Sides can be considered a group of allied teams.
Potential benefits of introducing sides:
I don't see Sides as an 0.2.0-alpha feature, but rather as a 0.3.0-alpha. This is up for discussion, however.
Letting teams listen to each others' statuses could prove useful in determining the current state of the battle.
Though not every game is going to want it, a pre-battle phase could be useful. We'll want to explore this, especially in regards to default turn managers.
Many games allow characters to perform multiple types of actions in a turn before getting tapped. Storing this as an integer would grant much more flexibility than a binary "is (not) tapped." New methods should include ConsumeAction(int)
and perhaps ConsumeAllActions
.
There are a lot of possible turn schemes, but provided some simple concrete turn schemes could help expedite the development of turn-based games.
This issue focuses on creating a turn-manager that will work for games where the player orders all units to act until all are tapped, like in Fire Emblem. Once all units have acted or the player ends their turn, priority is handed to the next team in the list.
Tests are an important part of software development (obviously).
Many game engines and frameworks don't allow devs to take total control of the game loop. Currently, the Arena only works from the inside - nothing from the outside can pass in input. Once DoBattle
is called on the Arena, the arena enters an infinite loop; that is, it becomes the game loop.
This behavior should still be supported (as it seems to work fine for text-based games), but there needs to be a way to have the Arena exist separately from the core game loop.
This will probably introduce several major API changes from 0.1.0-alpha.
Being able to index teammates directly instead of having to rely on solutions like Linq would be a huge benefit. I also can't really think of any good reason why a team would want to hold enemies as a set, map, or so forth.
By letting stats be serializable, devs can save stats or manipulate them easier in Unity.
Currently Spite is still listed as 0.2.1-alpha. It should be updated to the actual version.
This is going to look different for each type of turn scheme. In general, it is going to advance when each team or each unit has been able to act at least once. In other turn schemes, however, it might not make sense for each turn scheme to say which turn number it is. Perhaps the best way to accomplish this is through an ITurnCounter
interface that turn managers can implement if needed (and in general, they do). Furthermore, there should be an event that's triggered when the count goes up.
Unity 2021.2 has been released, which brings full C# 8 support and limited C# 9 support. The features from C# 9 that Unity has don't seem too useful to me at the moment, so I think it's best to limit the language version to C# 8 so the compiler can tell us what's good and what's not in any given profile.
This will also require us to update the Unity package.json file to change the minimum supported version of Unity to 2021.2.
The way it stands now, Teams and Sides are used interchangeably in the framework. This isn't ideal, since everywhere where Sides is used, an ITeam object is expected.
In the framework itself, the phase "Side" occurs ~20 times; "Team" occurs far more frequently. Since "Side" variables and methods accept ITeam objects, I believe it would be better to replace the term "Side" with "Team". This way, we can also reserve the term "Side" for something different in the future if need be.
This would be a simple override that could prove handy.
This can help make the determination of a team's own status easier and more up-to-date.
I envision this as an event on the ITeammate interface and the Team can register itself to.
Questions to answer:
It might be nice to be able to create battle end conditions as a class that can immediately end the battle when the conditions are met in addition to or in place of the current methods. This will require some exploration.
Currently, when a team queries an arena for any team, it needs to cast the team into the expected team implementation. By making Arenas somewhat generic, like Arena<T> : IArena where T : ITeam
, teams can reasonably query the Arena for teams without needing to cast to the specific type.
This will introduce several API changes and raises a few questions and concerns:
IArena
interface be genericized as well? Or should we have a non-generic arena?The following changes will also need to be made:
ArenaBuilder
class will also need to become generic.Before we get that wonderful INumeric
interface with .NET 6/Unity, I think having some int stats would be useful.
Some of the int stats I would like to see include:
Others that I think of can be added here.
There might need to be a mode enum, but we can tackle that if the feature is requested. Personal preference is to untap with the phase change.
Currently, TeamBuilder
's Start()
and Finish()
methods are templates accepting a type that extends ITeam
.
I believe it would make more sense for the TeamBuilder itself to be a template class, simplifying the team creation process.
With the removal of ITappable entity in 0.2.0-alpha, there's no reason for this type or even the namespace it exists in.
This will be useful for starting animations when values change and so forth.
Regardless of the turn scheme, every game has a notion of who's "turn" it is (the exception is with games where turns are taken simultaneously, but in that case, it's everyone's turn for the most part). I think that the best way to handle this is by implementing a DoesTeamHavePriority
or some similarly named method that accepts a team and returns true. In default turn schemes where entities take the priority, the method can check the teammate's team. Making this a virtual method would allow custom implementations of this determination. Adding a method to the arena that delegates to this would also prove useful.
#12 might benefit from this.
Currently, having examples tied to the main repository isn't that great. Move the examples to another repository will allow this repository to be more lightweight. This link on separating repositories might be useful.
The following things need to have unit tests associated with them:
TurnManager
s are invalidDiscretePlayerTurnManagers
Actions should be triggered for any pull request, not just merges into a specific branch.
Having the main documentation in the README bloats the readme and is hard to navigate. A project wiki would be a better place for this information.
A lot of exception messages are used in Spite that depend on the .NET resources functionality. However, since Unity can't use these, when an exception is thrown by Spite, the runtime can't find the proper resource for the message, which throws another exception, burying the original exception under another (in fact, the only way to figure out what the original exception was is to look at the code where the new exception was thrown from). This happens when Spite is included as a package (with source code). Even should this not be the case when using Spite.dll in Unity, this greatly increases the difficulty of using Spite in Unity.
In a game like Chess or Battleship, this type of turn scheme would make more sense.
It would be nice to have full releases of Spite published to NuGet automatically. I believe this is possible.
There are some common commands that should be included by default, such as a TapAllUnitsCommand
and a corresponding action. This is mostly applicable for discrete team turn schemes. It would tap all units on the team, ending the turn for the player issuing the command. The reason I don't call this an EndTurnCommand
is because ending a turn will look different for different turn schemes.
We need to make sure that there are:
Currently, Spite only supports .NET core and not the .NET framework. This limits the kinds of applications that Spite can be used with.
I don't want new versions of Spite to break Unity projects due to C# features that Unity may not yet support.
I think the easiest way to do this is to set the project's C# version to one that matches the lowest supported Unity project. This allows us to not have to set up some kind of pipeline and have the validation at compile time.
When major versions of Spite are released, we can drop support for older Unity versions then. This may also be a good time to revisit what the minimum version of Unity we want to support is.
In previous versions of the framework, there was a pipeline for .NET Framework. However, when I converted the project to be primarily .NET Core, I didn't keep the old GitHub pipeline for .NET. We should reintroduce that pipeline and include a badge for that in the README.md.
In this particular example, the offending code can be found on line 42 of TeamPhase.cs
:
return command.Executor.ResponsibleTeam == CurrentTeam &&
(command.Executor as ITappableTeammate)?.IsTapped != true;
There should be a null check on the command.Executor
property here. If the null check fails, I think raising an exception (such as a NullObjectException
) with a message something along the lines of "The command's executor has not been set yet" would be great.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.