Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
If you're looking for a jump-start on how to make games with FlatRedBall and Glue, you've come to the right place. The following tutorials will walk you through how to make a game called Beefball. Beefball is a fast-paced physics based game similar to air-hockey. What are you waiting for, let's get started!
Completed Project: Beefball.zip
This tutorial is an introduction to making games with FlatRedBall. It covers using the FlatRedBall Editor and writing code in C#. The FlatRedBall Editor is a program which helps with the creation and organization of game projects. We'll be exploring its features by creating a game called Beefball - a multiplayer competitive game similar to air hockey. When finished our game will have two circles, each movable with either the keyboard or an gamepad, and a smaller circle which each player can use to earn points.
The first step in any game project is to open up the FlatRedBall Editor. If you've downloaded and unzipped the FRBDK.zip file, then you should already have this on your machine. Unzip the file, and double-click Run FlatRedBall.bat.
If you haven't yet downloaded the FRBDK.zip file, you can get it from the Download page.
Once you open the editor, you can create a new project. To create a new Project:
Select File -> New Project
Enter Beefball for the Project Name.
Leave Desktop GL as the platform. Our game targets this platform because it is easy to debug. Creating the project for a desktop platform is recommended even if the game is intended to run on non-desktop platforms (such as Android). Additional platforms can be added at any time.
Uncheck Open New Project Wizard. We'll make Beefball "from scratch".
(Optional) Change the location of the project. By default the project is created in Documents\FlatRedBallProjects.
Click the Create Project! button to create the project.
The latest FlatRedBall template is downloaded, so your project runs against the newest version available. Now that you've made a project, FlatRedBall remembers this and automatically open it for you next time it is started.
FlatRedBall Editor is a tool meant to work hand-in-hand with Visual Studio. It is not a replacement for Visual Studio, meaning you will be doing work in both Visual Studio and the editor. It is quite common to develop FlatRedBall games with both Visual Studio and the FRB Editor open at the same time.
FlatRedBall projects automatically create a Visual Studio project too. To open the project in Visual Studio, click the Visual Studio icon as show in the following image:
FlatRedBall uses the windows default file association for your .sln file. If you would like to change this association, you can right-click on the .sln file in Windows Explorer to change the default file association.
You can also open the project in visual studio by opening the .sln file. The project folder can be opened by clicking the folder icon in the task bar. This opens the location of the .csproj file, which is one folder below the .sln file. The following animation shows how to navigate to the solution:
When you double-click the .sln file you may see a window like this:
If so you should select the version of Visual Studio that is compatible with the type of project you are running. At the time of this writing, Visual Studio 2022 Community is the most common version to use with FlatRedBall. Once Visual Studio is open, you can run your project by pressing the "start" button, or by pressing F5.
Your game should run if all prerequisites have been properly installed. You should see a blank game
That was easy! So far you have a fully-functional game using FlatRedBall. The next tutorial covers making our first Entity.
So far we have a basic structure for our game: an Entity with graphical representation, a Screen, and an instance of our Entity in our Screen. This tutorial requires writing code.
Before implementing code to control your Entity you need to decide how to control an Entity. For this tutorial we will implement controls using gamepads as well as keyboard (in case you do not have a gamepad). We will use FlatRedBall's input interfaces so that our game code doesn't need to consider which input device is using after initial setup. Note that FlatRedBall uses the Xbox36GamePad name since FlatRedBall was originally written to work with XNA. Most controllers will work including wired and wireless XBox controllers, Switch Pro controllers, and Playstation DualShock (although usually the DualShock requires a wired connection).
First we need to define which controls are needed in our game. Our PlayerBall requires the following input:
Movement in the X and Y coordinates - also known as "2D input"
Input for executing a boost. Boosts temporarily increase the player's speed when executed for a short amount of time. The user does not need to hold the input down, simply pressing the button/key is enough.
We'll write the PlayerBall so that it works with any input device, whether that's Xbox 360 controller, Keyboard, or any other device. To add code to PlayerBall, double click PlayerBall.cs in Visual Studio. This is located in your project's Entities folder.
Modify the PlayerBall.cs file so it contains two input properties as follows:
Next we will add code to move the player ball. Notice that there are four methods in PlayerBall.cs:
CustomInitialize
CustomActivity
CustomDestroy
CustomLoadStaticContent
CustomActivity gets called every frame, so we can use it method to respond to controller input and modify our PlayerBall appropriately. Add the following code in the CustomActivity method in PlayerBall.cs:
At this point we have written fully-functional code for moving the ball according to its MovementInput; however, we haven't assigned the MovementInput yet. We'll do this next.
The MovementInput and BoostInput were intentionally created as public properties so that they could be assigned by the GameScreen. The GameScreen will contain logic for deciding which input device to use. To assign the input interfaces, open GameScreen.cs in the Screens folder and modify the CustomInitialize method as follows:
Notice that the object we are assigning code to (PlayerBall1) matches the name of the entity in the editor. FlatRedBall objects in the editor always have a matching name in code, as shown in the following image:
For more information on the Keyboard class, see the Keyboard page.
Clean code is very important. This is something we stress at FlatRedBall for all developers making any kind of game regardless of size. Therefore, this tutorial (and others in the future) discusses how code can be improved to be more flexible and maintainable. The code we wrote above has a number of problems:
The velocity (which was set to 10) is set right in the method where it's used. This velocity value is typically considered "data", while its application is considered "logic". The separation of data from logic is a fundamental concept in keeping game projects maintainable.
The game includes logic in the CustomActivity method. We encourage no logic, only method calls in the standard "Custom" methods.
FlatRedBall provides a number of ways to separate data from logic. The simplest of these is to create variables. To add a variable to the PlayerBall entity:
In the editor, click the PlayerBall Entity
Right the Variables tab
Click the Add New Variable button
Verify that float is the Type
Enter the name MovementSpeed and click the OK button
Verify Variables is selected and set MovementSpeed to 100. Deselect the text box or press ENTER to apply the value.
Finally, return to the movement code inside PlayerBall.cs and change the code to:
If we run the game now we can control the player with the W, A, S, and D keys:
The benefit of using the input interfaces (I2DInput and IPressableInput ) is that the input device being used can be set or changed without any code changes in the entity. For example, we can modify the GameScreen to optionally use an gamepad if connected, otherwise it falls back to using the keyboard. To add support for keyboard and gamepad controls, open GameScreen.cs and Modify CustomActivity as follows:
For more information on Xbox360GamePad, see the Xbox360GamePad page.
Finally, we should clean up CustomActivity. In general, we encourage keeping the Custom methods free of logic so that it is clear what an Entity/Screen is doing when initialized and when destroyed without having to mentally translate code into concept. Open PlayerBall.cs and replace the CustomActivity with the following:
Then define MovementActivity in PlayerBall.cs file:
Similarly, we'll want to clean up the CustomInitialize method in GameScreen.cs:
We'll implement the AssignInput method in GameScreen.cs:
Now we have a PlayerBall Entity which is cleanly written, has speed which can be customized through the FlatRedBall Editor, and can be moved with the game pad or keyboard. The next tutorial covers defining collision in the GameScreen.
So far we have a very simple project with a PlayerBall (which appears as a Circle) which can be moved with a gamepad or the keyboard. This tutorial covers creating collision for the game. This tutorial introduces two new types.
ShapeCollection - an object which can contain any number of shapes. Adding multiple shapes to a ShapeCollection makes the creation of collision relationships very easy.
AxisAlignedRectangle - this is a rectangle shape which can be created in Glue just like We'll use multiple AxisAlignedRectangles to assemble our level, and we'll be organizing these in a list object.
First we'll create a ShapeCollection called Walls. This ShapeCollection contains all of the rectangles in our game. ShapeCollections are a specialized list which can contain multiple instances of Shape objects such as Circles and AxisAlignedRectangles. To create the Walls ShapeCollection:
Select GameScreen
Click the Quick Actions tab
Click Add Object to GameScreen
Select ShapeCollection as the type
Enter the name Walls
Click OK
Now that we have a ShapeCollection for our walls, we can add the individual walls. To add the first wall AxisAlignedRectangle:
Right-click on the Walls item.
Select Add Object
Select AxisAlignedRectangle as the type. Notice that available types are limited to shapes since we are adding to a ShapeCollection.
Enter the name Wall1 and click OK.
Now we can modify the properties of this wall. Select Wall1 and change the values as follows:
Y = 300
Width = 800
Height = 30
Doing so moves the rectangle to the top of the screen. If you run the game you should see the wall at the top of the screen.
Now that we've created a single wall, we can duplicate it by right-clicking on it and selecting the "Duplicate" command:
Create 5 duplicates, so that the Walls list holds a total of 6 AxisAlignedRectangle instances:
Next set the X, Y, Width, and Height variables for for the newly-created walls as follows: Wall2
X = 0
Y = -300
Width = 800
Height = 30
Wall 3
X = -400
Y = 200
Width = 30
Height = 200
Wall 4
X = 400
Y = 200
Width = 30
Height = 200
Wall 5
X = -400
Y = -200
Width = 30
Height = 200
Wall 6
X = 400
Y = -200
Width = 30
Height = 200
When finished, the game should have walls made of AxisAlignedRectangles with gaps on the left and right sides for goals:
Next we'll use collision relationships to define collision between the PlayerBall and Walls. We'll revisit collisions in later tutorials, so keep in mind that this is only the first step in setting up our game's collision. When we created our PlayerBall entity in an earlier tutorial, we marked that the entity should contain a Circle object. Doing so automatically marked the Entity as using the ICollidable interface. As a reminder, the following image shows the window we used to create the PlayerBall entity:
Since our PlayerBall is an ICollidable, that means it can collide with any other ICollidable and with any shapes - like the Walls. We need to tell our game that we want the PlayerList to collide against the PlayerWalls. The easiest way to do this is to create a Collision Relationship in Glue. To do this:
Drag+drop the PlayerBallList onto the Walls in the Glue explorer. This creates a new CollisionRelationship
Select the new CollisionRelationship (which is named PlayerBallVsWalls)
Select the Collision tab
Change the Collision Physics to Bounce Collision so that our PlayerBall bounces against the walls
Change the PlayerBall Mass to 0. This means that the PlayerBall behaves as if it has no mass, so it will not be able to move the walls. Be sure to press Enter or Tab to save the change.
If we run the game now, the PlayerBall collides with the walls.
We use CollisionRelationships here because they are very powerful and require no code. Just like everything else in Glue, collisions can also be managed in code. If you are interested in writing collision purely in code, see the CollideAgainstMove page.
The CollisionRelationship we created in the previous step set the Collision Physics to Bounce Collision, but if we move the PlayerBall to the wall, it doesn't bounce. While this may seem like bounce collision is broken at first glance, the real reason is because of the way our movement has been implemented. Our code in PlayerBall sets the velocity values every frame. When the PlayerBall collides against the wall, the bounce physics sets the velocity values to simulate a bounce; however, our code in PlayerBall overwrites this velocity immediately. We will make changes in a later tutorial to our movement code so we no longer overwrite the velocity every frame, and so the bounce movement works as expected.
Even though the game isn't really playable yet, we're definitely starting to see something come together. We have a movable object and game boundaries which have solid collision. The next tutorial duves deeper into the control of our PlayerBall Entity.
This tutorial covers how to create an Entity, which is the FRB term for a "game object". Examples of entities include:
Game characters (like Mario)
Projectiles (like a bullet)
Power-up (like a health pickup)
Our first entity is called "PlayerBall".
To create an Entity:
Click the Add Entity in the Quick Actions tab...
...or right-click on the Entities folder and select Add Entity
Enter the name PlayerBall
Check the Circle checkbox under the Collisions category. This adds a circle object to the PlayerBall entity, which we'll use to test if it is touching the walls, goals, or other ball instances.
Notice that the ICollidable checkbox is checked - we'll cover this in a later tutorial. We'll leave it checked for now.
Notice that Create Factory is also checked. This option simplifies the creation of additional entities in code. We'll leave this checked as well.
Click OK
Our entity is now created with a Circle named CircleInstance under its Objects folder, as shown in the following image:
The previous section showed how to create an entity and add a Circle to the entity at the same time. Objects can be added after an entity is created as well. Note, the following steps are only shown for example, and do not need to be followed if you performed the previous steps. To add a Circle to an already-created entity:
Click the Add Object quick action...
...or right-click on Objects and select Add Object
Select the FlatRedBall Or Custom Type option
Select Circle in the list
Enter the name CircleInstance and click OK
When a new Entity or Screen is created, a number of files are created:
<EntityName>.glej or <ScreenName>.glsj
<EntityName>.cs
<EntityName>.Generated.cs
For example, the PlayerBall entity creates PlayerBall.glej, PlayerBall.cs, and PlayerBall.Generated.cs. These files can be viewed by right-clicking on the entity and selecting View in Explorer.
The PlayerBall.glej file is a JSON file which stores all of the information for the PlayerBall. This file does not need to be edited manually in most cases since the FlatRedBall Editor provides controls for making changes. Of course, the file can be edited by hand, and any changes are automatically reloaded by the FlatRedBall Editor if the file changes.
This file should be included in version control so that the project can be opened and re-generated on other computers.
The PlayerBall.cs is a code file which contains custom logic. By default this file contains only empty functions, but these can be filled in to customize initialization, every-frame logic, destruction, and content loading. This file is often referred to as the custom code file. In a typical game, a lot of your game's code is written in screen and entity custom code files.
PlayerBall.Generated.cs is a file which is generated by the FlatRedBall Editor mirroring the contents of the JSON (glej) file. Any changes to the JSON file, whether through the FlatRedBall Editor or by manually editing the contents of the JSON file, results in the PlayerBall.Generated.cs being re-generated.
The contents of this file are re-generated if any change is made to the Player entity, or if the FlatRedBall editor is re-opened. In other words, generated files are re-generated by the FRB Editor, so any changes made directly to the generated file should be considered temporary. In practice this file can be used to help debug, or to perform temporary tests, but final changes should either be made in the JSON file or in custom code.
Generated files can optionally be included in version control. By default FlatRedBall excludes generated files since they can be re-generated from the corresponding JSON files. Of course, if the project is cloned to a new computer then the project must be opened in the FlatRedBall Editor to re-create all generated files. If this is problematic (such as if the development team includes individuals who do not open the FlatRedBall Editor), then the generated code files can be added to version control. Keep in mind that doing so may result in a larger version history.
At this point our project has a PlayerBall Entity which is ready to be used in a game. Of course, we haven't yet created an instance of the newly-created Entity, so if you run your game you won't see it (yet). The next tutorial creates a Screen which contains our PlayerBall Entity.
Screens and Entities are two common FlatRedBall concepts. A Screen represents a container for game content and other Entities. Screens define the flow of your game. Often game developers create many screens up-front to help think through a game's structure. Here are some examples of Screens in a typical game:
GameS play Screen (like the playing Screen in Pong). This is usually called "GameScreen"
Splash Screen (like a FlatRedBall logo displaying splash Screen)
Main menu Screen
As you work with Screens you will find that they are very similar to Entities. To create a Screen:
Click on the Screens folder and select the Add Screen quick action...
...or right-click on the Screens folder and select Add Screen
Uncheck Add Map LayeredTileMap option - Beefball doesn't use Tiled maps
Accept the other defaults by clicking OK.
Notice that FlatRedBall suggests the name GameScreen for your screen. Recommended practice is to always have the screen where your game takes place called GameScreen. If your game has multiple levels, each level would inherit from GameScreen. Since Beefball does not have multiple levels, we only create GameScreen.
By default, your GameScreen now has a PlayerBallList - this is a list which will contain all PlayerBall instances in your GameScreen.
If you unchecked the Add Lists for Entities option, or if you would like to know how to create lists manually, follow these steps:
Select the PlayerBall entity
Click the Quick Actions tab
Click the Add PlayerBall List to GameScreen button
The PlayerBallList object will contain all of the PlayerBalls we plan on adding later. Our game is a two-player game, so it will eventually contain two PlayerBall instances. The PlayerBallList object will be used to define collision relationships in a later tutorial. Collision relationships define which objects can collide with each other (such as players vs the walls) and what to do when they collide (such as performing bounce physics).
Once you have at least one Screen in your game (GameScreen), you can add Entity instances to that Screen. Entities can be added through the editor or through game code. The editor provides a number of ways to add an entity:
Drag+drop an entity on a screen to add an instance...
... or add an instance to the GameScreen by selecting PlayerBall and clicking the Add PlayerBall Instance to GameScreen quick action. Note that this option will only exist if you have a Screen called GameScreen...
... or add an object to a screen by right-clicking on the GameScreen's Objects folder:
Right-click on your GameScreen's Objects folder
Select Add Object
Select Entity as the object type
Select PlayerBall as the type. The name will automatically be changed to PlayerBallInstance
Click OK
Now that you have a PlayerBall instance in your GameScreen, you can run the game to see it. You can run the game through either the FlatRedBall Editor or Visual Studio.
To run the game through the editor, click the Play button in the toobar at the top
To run the game through Visual Studio, click the Visual Studio icon to open the game in Visual Studio and run it like any other desktop project
Your game should now be open in Visual Studio.
Once the game runs, you should see a circle (the PlayerBall1 instance) in your Screen.
Now that we have an object in our screen we can take a moment to understand how the coordinates in FlatRedBall work. By default, our entity exists at X=0 and Y=0. We can observe this by selecting the PlayerBall1 instance and looking at its Variables tab.
By default the center of the screen is at the origin (0,0), and objects are positioned by their center, so the PlayerBall appears at the center of the screen.
To recap we now have an Entity called PlayerBall which has a Circle. We've also created a GameScreen which contains an instance of our PlayerBall. If we run our game, it shows a white circle (our PlayerBall instance).
We're now ready to start adding some code to our project. The next tutorial covers controlling your Entity's movement.
Beefball is intended to be a competitive multiplayer game. So far we only have one PlayerBall instance, so let's add some more PlayerBall instances. Previously we added a list for our PlayerBall. We can now add a second PlayerBall with minimal changes to our project.
To add a new PlayerBall:
Expand the GameScreen's Objects folder
Select the PlayerBallList object
Select the Quick Actions tab
Click the Add a new PlayerBall to PlayerBall List. Alternatively, you can right-click on the PlayerBallList and select Add Object
Change the new PlayerBall's X value to 180
You should now see two PlayerBall instances under the PlayerBallList and in game. Also, since we created our collision relationships between the lists, the new PlayerBall can already collide against the walls and the Puck.
Now that we have two PlayerBall instances, we need to add a new collision relationship. This time, we will create a collision relationship between the PlayerBallList vs itself. To do this:
Select the PlayerBallList
Select the Collision tab
Find the PlayerBallList in the list of items
Click the Add button
Set Collision Physics to Bounce
If you run you game now, the two PlayerBall instances collide against each other. Also, if we added more players (a third or fourth player) those would also collide with each other automatically.
We'll assign input on PlayerBall2Instance with code similar to the input-assigning code for PlayerBallInstance. To do this, open GameScreen.cs and modify AssignInput as shown in the following code:
Now each PlayerBall uses a different Xbox360GamePad or set of keys.
Now that we have multiple PlayerBall instances, we have a game that is playable, but it's missing scoring and game rules. The next tutorial adds the ability to score goals.
This tutorial implements more advanced controls for the PlayerBall. Most games with first-person control (that is control where you directly direct an Entity, as opposed to third person control like RTS games - not to be confused with first person view) have fairly complex control logic. We'll be implementing more advanced controls in our game, investigating the logic in detail along the way.
Our current implementation provides immediate control over the PlayerBall's velocity values:
In other words, if the input for moving right is held (whether that's a keyboard key or an Xbox360 analog stick), the PlayerBall immediately moves at maximum speed. Similarly, when the movement input is released, the PlayerBall immediately stops. As mentioned earlier, this logic prevents the collision relationship from making the ball bounce. We will modify our input code to gradually add to the speed of the PlayerBall when the input is held down. To do this, we'll change our code to set the PlayerBall's acceleration rather than velocity. Modify the MovementActivity in PlayerBall.cs as follows:
Notice that the code above still uses the MovementSpeed variable, which can be modified in Glue. This value can be increased to make movement more responsive. You may want to increase this value from 100 to a larger number such as 300. Now our ball can bounce against the walls, and it doesn't immediately speed up or slow down - it takes some time to gain speed.
Since we're no longer setting velocity values directly (acceleration values add and subtract to the current velocity), the ball continues to move even after releasing input. We'll address this in the next section.
Select the PlayerBall Entity in Glue
Select the Variable tab
Click the Add New Variable button
Select the Expose an existing variable option
Click the Drag variable
Click OK
Change the Drag variable to 1
The addition of Drag has changed the way our ball moves:
The ball now has a maximum speed
Releasing all input results in the ball slowing down
The ball does not accelerate as quickly as it used to
We'll increase the MovementSpeed to make up for the addition of Drag on the PlayerBall's acceleration. Feel free to play with the MovementSpeed variable. A value around 350 should result in responsive movement.
The previous tutorial created a collision relationship between the PlayerBall and Walls. Now that we have bouncing implemented, we can revisit the PlayerListVsWalls collision relationship. Notice that the relationship provides an Elasticity value as shown in the following image:
This value controls how much velocity is preserved after a collision occurs. A value of 1 indicates that 100% of the velocity is preserved. A value less than 1 results in some of the velocity being absorbed. Feel free to play with this number to create a bounce elasticity that you like. If you want the balls to remain bouncy, you can keep it at 1. If you want the walls to absorb some of the PlayerBall velocity, try putting a (positive) value less than 1, such as 0.5.
Now we're getting somewhere! The game is starting to feel pretty solid. Next we'll add a Puck Entity which can be used to score goals.
Now that we have our PlayerBall movement working, we'll add a Puck Entity which the user can hit around. You'll find that the Puck is very similar to the PlayerBall.
To create a Puck Entity:
Click on the Quick Actions tab
Click the Add Entity button
Name the Entity Puck
Check the Circle check box under Collisions
Verify that ICollidable is checked (it should be checked automatically when Circle is checked)
Click OK
The Puck entity should appear in the FlatRedBall Editor.
Currently our Puck and PlayerBall both have Circle bodies, and by default the Circles have the same size and color. To differentiate the Puck from the PlayerBall:
Expand the Puck entity
Click on the CircleInstance object under the Puck Entity
Click the Variables tab
Find the Color variable
Change the value to Red using the drop-down
Change the Radius value to 6
Computer settings matter: If your computer is set up so the decimal separator is the comma ',' instead of the period '.' then you should enter values using the ',' character. Unlike C# code, Glue obeys your computer's language settings.
By default the FlatRedBall Editor adds lists of newly-created entities to the GameScreen. Therefore, you should already have a PuckList in your GameScreen.
If you unchecked the option, or if you would like to know how to manually add a PuckList to your GameScreen, the following section shows how to add a list. This is not necessary if you kept the defaults.
Click the Puck entity
Select the Quick Actions tab
Click the Add Puck List to GameScreen button
Select the Puck entity
Click the Add Puck Instance to GameScreen button
Now the GameScreen has a list and a single Puck.
If you run your game you'll notice that the PlayerBallInstance and PuckInstance are both at the center of the Screen. Let's reposition the PlayerBall1:
Select the PlayerBall1 object under your GameScreen
Change the X value to -180
Now that we have a Puck in our game, we need to create two collision relationships:
Puck vs Walls - this prevents the Puck from moving through the walls.
Puck vs PlayerBall - this allows the PlayerBall to "hit" the puck to try to score a goal. We haven't yet created the handling of goals, but this is the first step towards implementing that feature.
We create these two collision relationships just like the previous PlayerListVsWall collision relationship. To create a relationship between the PuckList and Wall:
Expand GameScreen's Objects folder in Glue
Drag+drop the PuckList onto the Walls object
Select the new PuckVsWalls collision relationship
Select the Collisions tab
Set the Collision Physics to Bounce
Change the Puck Mass to 0
Optionally adjust the Elasticity value
To create a relationship between the PuckList and PlayerBallList:
Expand GameScreen's Objects folder in Glue
Drag+drop the PlayerBallList onto the PuckList object
Select the new PlayerBallVsPuck collision relationship
Select the Collisions tab
Set the Collision Physics to Bounce
Change the Puck Mass to 0.3 - this makes it 30% the mass of the PlayerBall
Optionally adjust the Elasticity value
Notice that the mass variables for PlayerInstance vs. PuckInstance differ compared to wall collision. The PuckInstance is given a mass of .3 relative to a mass of 1 for the PlayerInstance, resulting in the PuckInstance behaving as if it has 30% of the mass of the PlayerInstance. If you run the game, you should be able to hit the Puck around the level.
Currently the Puck moves indefinitely after being hit. We'll assign the Drag value to the Puck just like we did to PlayerBall:
Select the Puck Entity in Glue
Select the Variables tab
Click the Add New Variable button
Select the Expose an existing variable option
Select the value Drag
Enter a value of 0.4 for Drag
Now the Puck slows down over time just like the PlayerBall.
Currently the game is playable, but scoring a goal results in the puck moving off-screen. We'll add logic and data to detect when a goal is scored by resetting the position of all objects and assigning points.
Conceptually detecting a goal is simple - whenever the Puck collides with a certain area then a goal has been scored. The first step is to define the goal area. First we'll create a Goal entity:
Select the Quick Actions tab
Click the Add Entity button
Name the entity Goal
Select the AxisAlignedRectangle option under Collisions
Click OK
The default rectangle size for a Goal is too small, so we should make it bigger:
Select the AxisAlignedRectangleInstance in the Goal entity
Click the Variables tab
Change Height to 200
Just like before, we'll also add a list of goals in our GameScreen:
Select the Goal entity
Select the Quick Actions tab
Click the Add Goal List to GameScreen button
Next we'll add two goal rectangles to the GameScreen. Instead of using the Quick Actions tab, we'll show an alternative method here by drag+dropping the Goal entity onto GameScreen. Do this twice, changing the name of the first to LeftGoal and the second to RightGoal:
Why do goal names matter? In an earlier tutorial we created the wall collision and named them sequentially (Wall1, Wall2, etc). However, the goals are slightly different - we will be checking which goal we collided with in code to determine whether the left or right team should earn a point. Setting clear names makes it easier to keep track of which goal is on each side.
Next, modify the goals values as follows: LeftGoal:
X = -410
RightGoal:
X = 410
If you tried playing the game, you may have noticed that the PlayerBall instances can leave the screen by passing through the goals. To fix this we can create another CollisionRelationship between the PlayerList and GoalList:
Expand the GameScreen's Objects folder
Drag+drop the PlayerBallList onto the GoalList object
Select the new PlayerBallVsGoal collision relationship
Click the Collision tab
Change the Collision Physics to Bounce Collision
Set the PlayerBall Mass to 0
If you run the game, you can no longer leave the play area with either PlayerBall.
Now that we have all of our data and object instances set up, we can write code to detect if a goal has occurred. First we'll need two variables to keep track of score. Add this code to GameScreen.cs at class scope (outside of any methods):
Now we'll create another collision relationship, but this time we won't use any physics. Instead, this will be an event only collision relationship. This means that when the relationship detects a collision, it raises an event (call a function) which allows us to perform custom logic such as increasing score and resetting the game. First we'll create a collision relationship:
Expand the GameScreen's Objects folder
Drag+drop the PuckList object onto the GoalList
Now we'll create an event, which is a function that is automatically called whenever the collision occurs:
Select the new PuckVsGoal collision relationship
Select the Collision tab
Scroll to the bottom and click the Add Event button
Accept the defaults and click OK
When an event is added, Glue automatically adds a new file to contain events called GameScreen.Events.cs. We can find the new event there. You need to expand the GameScreen.cs in Visual Studio's Solution Explorer to see this file.
We can check which Goal collided with the Puck in the code and perform different logic. We can Modify the OnPuckVsGoalCollided method and add a new ReactToNewScore method to the GameScreen.Event.cs file as shown in the following snippet:
Whenever a Puck collides with either goal, we call ReactToNewScore, which resets the player and puck positions and velocities back to their starting states. Notice that the code in GameScreen.Event.cs has access to the player1Score and player2Score variables. This is because the GameScreen is one class split into multiple files using the partial keyword. Therefore, anything you define in Glue or GameScreen.cs is available in GameScreen.Event.cs.
If you've made it this far, congratulations! You now have a fully-playable game...at least, to the point where you can play with friends. There's still a few more things we'll do to improve the design and add polish. The next tutorial covers adding a HUD to display player score.
For more information on how to control the screen's resolution and world units, see the .
For more information on how to control the Camera to change the center of the screen, see the .
We'll use the property to slow the PlayerBall. Drag modifies velocity proportionally and in the opposite direction of current Velocity. In other words, drag slows an object regardless of its movement direction. The faster an object is moving, the more Drag reduces its velocity. Drag is a built-in variable on all Entities, so if you are creating a game which uses acceleration to move an object, you may want to also implement Drag. Drag is applied whether an object is accelerating or not. Drag slows an object in proportion to its absolute speed - the faster it moves the more Drag slows velocity. This results in a maximum speed, also known as terminal velocity. To add Drag to the PlayerBall Entity:
For more information on how to perform the above steps, you may want to review the tutorial which created the PlayerBall Entity .
Now that we have a visible HUD, we need to add logic to have it update when players score. This tutorial adds code to the ScoreHud to update the displayed text according to public properties which are set by GameScreen whenever a goal is scored.
First we'll be adding properties to the Hud object for the scores of each of the players. We could do this purely in code, but the FRB Editor provides a convenient way to set the score values to integers. We will be combining two FRB Editor features for this:
Tunneling variables - Creating a variable in an entity which is tied to the variable of a contained object. In this case we'll be creating variables which are tied to each of the score Texts' DisplayText variable.
Built-in casting of the variable type - although DisplayText is a string, we'll tell FRB that we intend to use it as an integer.
To do this:
Expand ScoreHud's Objects in the FRB Editor
Drag+drop the Team1Score onto the Variables item
Select DisplayText as the variable
Set the Alternative Name to Score1
Set the Converted Type to int
Click OK
Repeat the steps above, but this time use Team2Score, and create a variable called "Score2".
Now that the ScoreHud can react to score changes, the ReactToNewScore can be modified to update the HUD. To do this, modify the ReactToNewScore method in GameScreen as follows:
If we run the game now we'll notice that scores start out at 0 and increments whenever a player scores a goal.
If you are seeing a conversion error similar to Cannot implicitly convert type 'int' to 'string' , then you may need to modify the variables for Score1 and Score2. See the steps above where the Variable Type is set to int. If you've already created the variables, you can set the variable conversion:
Select the Score1 variable
Select the Properties tab
Change OverridingPropertyType to int
Well, that was easy! Now we have scoring working. At this point we could call the game done. The next tutorial adds some extra finishing touches to the controls to make it more competitive and make the game play deeper.
At this point Beefball is a fully-playable game, but we'll be adding one final feature: dashing. This provides deeper gameplay that rewards skill and adds an element of anticipation and excitement.
A dash gives the player a burst of speed in the current direction that the player is pointing. The dash can be used to hit other players, shoot the puck, or dive in front of the puck to block a goal. To add a dash, modify CustomActivity in PlayerBall.cs as follows:
Next, implement DashActivity in PlayerBall.cs:
If you try to build your game, you'll notice that the variable DashSpeed is undefined. This is a variable that should be defined in the FRB Editor so that it can be tuned easily:
Click on PlayerBall
Select the Variables tab
Click the Add New Variable button
Verify that float is selected
Enter the name DashSpeed
Click OK
Enter a value of 600 for DashSpeed
Your game should build and dashing should be fully functional.
Currently the player can dash indefinitely. We'll modify the dash so it has a cool-down after it's used. We'll need a variable to keep track of when the dash was first used, and compare against that variable to check if the user can dash again. First, add the following variables to your PlayerBall.cs at class scope (make it a field):
Next, add a check for the last dash time and a set to lastDashTime in DashActivity in PlayerBall.cs. Your entire method should look like:
Just like before, we need to create a DashFrequency variable in the FRB Editor:
Click on PlayerBall
Select the Variables tab
Click the Add New Variable button
Verify that float is selected
Enter the name DashFrequency
Click OK
Enter a value of 2 for DashFrequency to indicate a 2 second frequency
Now the player can only dash once every two seconds.
Why did we create DashFrequency and DashSpeed variables in the FRB Editor, but lastTimeDashed in Visual Studio? You may have noticed that we defined some of our variables (like DashFrequency and DashSpeed) in the FRB Editor, but we defined lastTimeDashed in Visual Studio. When working in the FRB Editor it is important to distinguish between "data" and "logic". Variables considered data are created in the FRB Editor so that they can be easily modified. Game development is an iterative process, and even the most experienced game designers make changes to their game throughout development. Creating variables in the FRB Editor makes changing variables easy, and communicates to other developers that these variables should be tuned. The lastTimeDashed variable, on the other hand, exists solely to support the logic of limiting dashing. The actual value of lastTimeDashed changes many times as the game runs, and setting it through the FRB Editor either has no impact on the game or introduces an unintended bug of making dash not work (if the value is set to a large positive value).
Now that the player's dashing is limited, we need to add some visible indication of when another dash can occur. We'll do this by adding a second Circle to the PlayerBall which grows when the user has dashed, then disappear when the user can dash again. To do this:
Click on PlayerBall
Select the Quick Actions tab
Click Add Object to PlayerBall
Select Circle
Enter the name CooldownCircle
Next we'll use States (something we haven't used yet) to define what the PlayerBall should look like when the cooldown first begins and when the cooldown ends. States have the following benefits:
They let you "code against concept, not content". In other words, you can define a state and use it in code, and later make changes to the state without having to modify any code. This is desirable because when you set the PlayerBall to a "Tired" state, your code shouldn't depend on anything except for the presence of a Tired state. Code should simply set the state to Tired, while the logic of Tired should be handled elsewhere.
States can interpolate from one to the other. In this case, we'll have the "Tired" state set the CooldownCircle to have a small radius, and the "Rested" will have a large circle. The end result is the CooldownCircle "grows" as the player's dash is recovering.
All states must be categorized, so the first step is to create a new category:
Right-click on the States item under PlayerBall and select Add State Category
Name the category DashCategory and click OK
Now the PlayerBall has a category named DashCategory.
Next we'll add states to the category:
Right-click on DashCategory
Select Add State
Enter the name Tired and click OK
Repeat the above steps to create a "Rested" state as well
As mentioned above, we'll want the CooldownCircle ball to grow (increase its Radius) to give the player an indication of how much time is left in the cooldown. To do this, we'll need to tunnel in to the CooldownCircle's Radius property:
Drag+drop the CooldownCircle object onto the Variables item
Select Radius for the Variable
Click OK
Categories must be told which variables to modify. By default, categories do not modify any variables.
To add a variable to a state, drag+drop the variable onto the category:
In this case, the only variable is the CooldownCircleRadius. Next let's define the two states:
Change Tired CooldownCircleRadius to 0.
Change Rested CooldownCircleRadius to 16. This should match the default radius for Body.
Now that our states are defined, let's use them in code. The simplest way to use states is to assign an entity's current state variable. FlatRedBall also provides functions for interpolating between states, which is the process of gradually changing from one state to another. We will write code to set the state to "Tired", then interpolate to rested over the course of two seconds - of course we'll use DashFrequency rather than hard-coding the value 2. To use these newly-created states, go to PlayerBall.cs and modify the DashActivity function as follows: To use the states, add the following two lines of code inside the if-statement in DashActivity in PlayerBall.cs:
Lets change the color of the PlayerBall to help tell the two players apart. We will "tunnel" to the Body's Color so that it can be changed per player instance.
In FlatRedBall, expand the PlayerBall entity
Drag+drop the CircleInstance object onto the Variables folder
Select Color as the variable.
Click OK
Repeat for the CooldownCircle's Color
Now that the color variables is exposed, lets change one circle in GameScreen
In GameScreen, choose PlayerBallInstance2
Change both CircleInstanceColor and CooldownCircleColor custom variables to Cyan, or the color of your choosing.
Run the game!
You can now tell the difference between each player.
If you've read this far, then you have officially just finished your first FlatRedBall game. Way to go! At this point, you can continue reading other tutorials, tweak this game more, or start on your own brand new game. Good luck!
This tutorial covers creating a HUD object which displays the scores of each player.
First we'll create an Entity to store all of our scoring information. We could place all of our HUD objects directly in the Screen, but this approach can result in a large number of objects in the GameScreen, making it difficult to maintain. We'll create a "Hud" entity to keep all HUD objects organized. To do this:
Select the Quick Actions tab
Click the Add Entity button
Enter the name ScoreHud
Click OK
For the ScoreHud we'll define the Text objects in Glue (just like we defined the body of the Puck in Glue). To do this:
Select ScoreHud
Select the Quick Actions tab
Click the Add Object to ScoreHud
Select Text as the type
Enter the name Team1Score
Click OK
Repeat the steps above to create another Text object called Team2Score
Repeat the steps above to create another Text object called Team1ScoreLabel
Repeat the steps above to create another Text object called Team2ScoreLabel
You should now have 4 Text objects:
Now we'll change the following variables on the Text objects in Glue. Select the following Text objects and set the variables as defined below: Team1Score
DisplayText = "99"
X = -150
Y = 270
Team2Score
DisplayText = "99"
X = 180
Y = 270
Team1ScoreLabel
DisplayText = "Team 1:"
X = -205
Y = 270
Team2ScoreLabel
DisplayText = "Team 2:"
X = 124
Y = 270
To add the ScoreHud to the GameScreen:
Select the ScoreHud
Select the Quick Actions tab
Click the Add ScoreHud Instance to GameScreen button
You should now see everything showing up correctly in your game
Now we have a score HUD that shows up when the game plays, but it doesn't react to scored goals. The next tutorial adds the necessary logic to have it react to scored goals.