Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
CSV (comma separated values) is a spreadsheet file format which has extensive support in FlatRedBall.
CSV stands for "comma separated values". If you are familiar with Microsoft Excel, then you may be familiar with the .xlsx or .xls file format. The .csv file format is similar to the .xlsx file format in that they are both spreadsheet file formats. One main difference between the two is that the .csv file format is much easier to read and edit in a text editor. For example, consider the following:
If this were a CSV file viewed in a text editor then it would appear as follows:
You can see above that the values (such as "Column 1" and "Column 2") are separated by commas. This is where the name "comma separated values" comes from.
When you create or modify a CSV which is part of your FlatRedBall project, FRB automatically creates code files (classes) which can be used to store information that is loaded from a CSV in your project. These files/classes are often referred to as "data" files and classes because FRB generates them in the Data namespace of your project. The data file will include a class with the same name as your CSV file. This will include members matching the headers of the columns of your CSV. For example, consider a file EnemyInfo.csv which contains the following:
If this file is added to a Screen, Entity, or Global Content, then FlatRedBall automatically creates a file called EnemyInfo.Generated.cs:
To add a CSV file:
Open the FlatRedBall Editor
Pick a place for your CSV file in your project. Usually CSV files are added to Global Content Files
Right-click on on the location which should contain the new CSV and select Add File -> New File
Enter the name for the new file. Although not necessary it's common to end the file name with "Data". For example, EnemyData if the file is to include data about enemies.
Once you have added a new CSV file you can edit it in any editor you prefer, such as Excel, Libre Office, or even a text editor.
CSV files which are added to the FRB Editor are automatically loaded. If your CSV is added to Global Content Files (as is usually the case), then its contents can be accessed through the GlobalContent object.
For example, consider an EnemyData.csv file added to Global Content Files.
This file can be accessed in code through the GlobalContent object in code as shown in the following code snippet:
The example above assumes a row for the "Monster" enemy and a column for Health as shown in the following image:
Note that if you add additional rows or columns you can access these in your code. Also note that this code assumes that the CSV is loaded into a dictionary. For more information on List vs Dictionary loading, see the CreatesDictionary property.
As mentioned above, the FlatRedBall editor automatically generate a code file based on the CSV. Specifically, FRB looks at the top row (headers) of the CSV file to determine the class members. If a header does not specify a type, then it will default to a string type.
The example above has two string members: Name and Texture. Headers can also define the type of a class. For example, the header above Speed (float) results in the EnemyInfo class including a Speed property of type float. FRB supports comments for headers resultingin the exclusion of the property. For example, the following CSV would include a single Name property, and the other column is ignored:
Columns in a CSV may be of a list type (such as List<int>). These types can be specified in parenthesis just like any other type. When a CSV includes a list, the required row defines how many entries are in the CSV. For example, the following CSV contains two car definitions, and each car has multiple features.
The generated code file for the data class is marked as "partial". This means that you can add additional code files to add additional variables and functionality to data that is created by FRB. To do this:
Find the data file in Visual Studio
Right-click on the folder containing the Data file
Select "Add"->"Class..."
Enter the name of your CSV file without the extension. For example, if using EnemyInfo.csv, the new Class name would be EnemyInfo (so it matches the class name in the generated file)
Once the file is selected, find the header of the file and make it partial. In other words, change:
to
Now you can things to EnemyInfo to help you work with the generated class. Note that this approach of using partial classes is the same as what FRB automatically does for you when you create a Screen or Entity. Custom code is very common in Screens and Entities, so FRB automatically creates a custom code file for you. Custom code in data classes is less common so FRB does not automatically do this for you; however if you do it manually it works the same way.
Each row in a CSV can be a basic type or an instance of a full class/struct. This tutorial shows how to add structs and classes to your CSV file.
Many games require data types which are more complex than can be identified with simple primitives. For example, you may be creating a game which needs enemy types defined. Each row in your CSV may represent an enemy type; however each instance may also need a non-primitive type to define the type of attack damage it does. If these classes were laid out in code, they might look like this:
As we'll see later in the tutorial, using classes and structs also allows for your CSV to benefit from inheritance.
First we start by creating the CSV that contains our information, then we'll explain the syntax. The following is a screenshot from Excel, but you can use any spreadsheet editing program (such as LibreOffice):
A few things are worth mentioning:
The Name property is marked as "required". This is common practice so that the CSV can be deserialized to a Dictionary.
The type for AttackInfo must be fully-qualified - in other words we use "MyGame.DataTypes.AttackInfo", not just "AttackInfo".
Each parameter can be assigned to any value valid for the type. Note that order does not matter (AreaOfEffect could be assigned before Damage), and it is not necessary to assign variables. For example, you could simply have "Damage=4" and AreaOfEffect would remain as its default value.
Types used by the CSV must be defined manually. FlatRedBall automatically defines the type that the entire CSV is using - in this case "EnemyInfo", however types used inside the CSV (such as AttackInfo) must be manually defined inside your current project.
Objects in CSVs also support inheritance. Inheritance allows you to define a base type for a property on your objects, but then specifying derived types in each individual row. First, we modify our data classes as follows:
Using the code above, we now have FireAttackInfo and SlashingAttackInfo, both of which inherit from AttackInfo. We can now instantiate any of those three types in our AttackInfo column. The following shows a CSV that instantiates different types:
When using inheritance we must specify the type that we want to use in each column. The syntax for this is the same as it is when instantiating an object in code - use the keyword "new" followed by the type. The contents of the parenthesis are the same as as before. You can assign values defined in the derived class as well as the base class. You can also leave out assignments that you want to keep as the default. For example the EndBoss enemy does not define the AreaOfEffect - it defaults to 0.
The CsvFileManager doesn't differentiate between types that you have defined and types that are defined by other libraries (such as types in the XNA libraries). This means that you can immediately add a lot of types of objects to your CSV without ever having to define them in your project. For example, the following shows how to define a column of XNA Rectangles:
UniformRowType can be used to load the CSV data in a more raw format rather than generating a custom class for the CSV. This is useful if the data in the CSV needs to be accessed by row and column index rather than by class member names. For this guide we'll be using a CSV with the following data:
Before changing the UniformRowType value, we should look at the code generated by Glue for this CSV. The class that contains the CSV will have a dictionary of objects as shown in the following code:
In this case the Csv class is generated as shown in the following snippet:
In this case we could access the CSV data through the CsvFile member. If the CSV were part of GlobalContent we could get the information as shown in the following code:
Note that we access the first column using the Column1 property.
We can change the UniformRowType to string[] to change how Glue generates the CSV code.
This setting tells Glue to generate each row in the CSV as a string[], allowing us to index into each column as shown in the following code:
Glue offers two options for UniformRowType:
String
String[]
The most commonly used value is String[] which tells glue that each row contains multiple values. Since each value in a row may need to be independently accessed, each row is represented as a string array. For example, to access index 3 in a row, your code may look like:
If your CSV has only one column, then the UniformRowType can be String. This can simplify accessing the row. For example, to access the first item in the row at index 2, your code may look like:
CreatesDictionary is a property that controls whether to deserialize a given CSV file to either a List or Dictionary. If you are accessing entries within a CSV according to some common key (such as the Name of an enemy type), then you should use the CreatesDictionary property to simplify access in code.
CreatesDictionary can be set on a CSV file in its properties tab or when first adding a new CSV. The following shows the option for specifying Dictionary or List when creating a new CSV:
Dictionaries in code require a key. This is typically the Name property which is marked with the required
keyword. For example, the following CSV marks the "Name" as required:
Name is common The most common key for data is "Name". Unless you have a reason to not use this, consider using "Name" as your required property.
If you have a CSV without a "required" field, then FRB cannot create a Dictionary out of the CSV. Just like any other CSV, once you have saved this file, the generated class will be added/updated.
CreatesDictionary can also be set to true in the Properties tab of a CSV file:
Now that you have created a CSV that has CreatesDictionary set to true, you can access the file in code and get instances using the Name property:
You may be wondering if it is a good idea to access objects using strings as the key. For example, let's consider the following line:
As you may have realized, this can be an unsafe line of code. What if, for example, the CSV changes so that it no longer contains the "Imp" object? Then that code would compile, and the error would only be discovered if that particular piece of code was executed. In other words, there is a risk of having hidden bugs associated to this line of code. First, we should mention that the reason the code above uses the hardcoded "Imp" value is to show the connection between code and the CSV. In final game code this is not good practice. Fortunately this type of code is usually not necessary. Let's look at the two most common situations where values are retrieved:
It is common to use values form CSVs in Lists. For example, let's say that the TestCsv used above is being used in a screen where the user picks which enemies to take into battle. Your code may look something like this:
This would dynamically loop through the list and create UI elements for each item in the CSV. If an item is added or removed, your code would automatically adjust to this and only show which values are appropriately.
In some cases you may need to access values directly. This can happen if:
You need to set a default value for something in code
You need to create a script in code, such as defining which enemies appear in a particular level
Fortunately the generated code creates const string values to allow you to access values in a compile-time-protected way. For example, the code above to access the imp could be more-safely done as follows:
The TestCsv class has one constant for each entry in the CSV, so the code above will work without any other code.
If a CSV is loaded as a dictionary, then FlatRedBall generates an OrderedList property. this property can be useful if you would like to have your data loaded in to a Dictionary while also providing its order. One example of this usage might be to include a list of data for each level in your game. This CSV could be used to populate a level selection ListBox which should show the levels in a particular order. Keep in mind that the Dictionary collection does not guarantee preservation of order, so if you intend to show your data in the same order as the CSV, be sure to use the OrderedList property.
Note that OrderedList is only generated if CreatesDictionary is set to true.
CSVs are often used to define data for games, but it's common for one CSV to need to reference information from another CSV. This tutorial will walks you through creating two CSVs, with one referencing the other.
We will be creating two CSVs:
The first defines the weapons in your game. The weapons defined for this game are for a traditional RPG.
The second will define the shops in the game which will contain a list of the weapons which they can sell.
First we'll create a CSV for weapons called WeaponInfo:
Right-click on Globa lContent Files
Select "Add File"->"New File"
Select "Spreadsheet (.csv)" as the type
Enter the name "WeaponInfo" as the new file's name
Click OK
Next create the CSV for StoreInfo:
Right-click on Global Content Files
Select "Add File"->"New File"
Select "Spreadsheet (.csv)" as the type
Enter the name "StoreInfo" as the new file's name
Click OK
You should now have 2 CSVs in your Global Content Files uner Global Content Files:
Fill the WeaponInfo file so it appears as follows:
Fill the StoreInfo so it appears as follows:
Note that the StoreInfo CSV file uses a List<string> instead of a List<WeaponInfo> for the WeaponTypes it contains. Specifically the StoreInfo references the Name of the WeaponInfo, similar to a key in a database.
Next we'll need to write some simple code to associate the values in the WeaponTypes column in the StoreInfo CSV to the weapons defined in WeaponInfo.csv. To do this:
Open your project in Visual Studio
Expand the "DataTypes" folder in the Solution Explorer
Right-click on the "DataTypes" folder and select "Add"->"Class..."
Name the class "StoreInfo". This should be the same name as your CSV.
You should now have a file called StoreInfo.cs and a file called StoreInfo.Generated.cs
Next, open the newly-created StoreInfo.cs file and make it a "public partial" class. After you do this, your code should look like:
Finally save your project through the "File"->"Save All" option in Visual Studio. This saves changes to the project so that if the FRB Editor is open it will reload the changes.
Next we'll add a property to the StoreInfo.cs file to make it more convenient to access which WeaponInfo's a Store contains. To do this, add the following code to StoreInfo.cs:
Now each ScreenInfo which is loaded into GlobalContent has an IEnumerable of WeaponInfo's that can be used to populate UI, menus, and drive game logic.
By default Glue creates a new class in the DataTypes namespace for every CSV in your project. For example, consider a game where each enemy can have multiple types of attacks. Each enemy might store the attack information in its own CSV file. In this example the CSV files would be called:
SoldierAttackData.csv
OgreAttackData.csv
SlimeAttackData.csv
ZombieAttackData.csv
We will assume that each CSV file has the same columns, and we would like to have the same code handle enemy attacks regardless of which type of enemy is performing the attack. However, by default Glue will create four classes - one for each CSV. To solve this problem, we can create a common class to be used by all CSV files. This can be done by right-clicking on each of the CSV files and selecting the Set Created Class option.
This example uses enemy entities which each provide their own attack data in CSV files. Each CSV has its Created Class set to Enemy Data.
A new class can be added in the Created Class window.
Once added, classes appear in the list below.
This class can be used for the current CSV by selecting the class and clicking the Use This Class button.
Once a class is added, Glue generates a file for this in the DataTypes folder.
The four CSVs specified above will deserialize into AttackData Dictionaries or Lists.
Glue automatically creates a class called PlatformerValues if a game includes platformer entities. Every entity will store its platformer values in instances of the common PlatformerValues class. Removing this class can cause compile errors.
Column 1 | Column 2 | Column 3 |
---|---|---|
Name (required) | Speed (float) | Texture |
---|---|---|
Name (required) | // Comment |
---|---|
Name (required) | Features (List<string>) |
---|---|
Boundaries (Microsoft.Xna.Framework.Rectangle) |
---|
The OrderedList property is a List of values which correspond to the order of values in a dictionary CSV. A given CSV must have its property set to true for OrderedList to be generated.
The OrderedList property can provide access to entries in a dictionary CSV when order is important. The reason this is necessary is because values in a Dictionary at runtime are not guaranteed to be in the same order as the values in a CSV. For more information, .
Dictionary order is undefined. The order of entries in a dictionary is not always the same as the order of entries in a CSV. For more information on retrieving the order, see .
Name (string, required) | Damage (float) |
---|
Name (string, required) | WeaponTypes (List<string>) |
---|