The Advanced Interpolation Plugin simplifies advanced interpolation between States. This plugin provides numerous interpolation methods which are applied using the standard InterpolateBetween method provided by Glue. The logic generated by this plugin is available through an overload to the InterpolateToState method. Just like the regular InterpolateToState method, the method generated by this plugin needs to only be called one time and interpolation will automatically happen over time with no additional custom code needed. In other words, if you are familiar with the InterpolateToState method provided by Glue, you will find this method very similar in usage.
If you have been using Glue for your projects then you may be familiar with states. If not, you should start by checking out the States page. Since states can set numerical values, that means that multiple states can be combined to create an intermediary state at runtime. For example, consider a State called "Transparent" which sets the Alpha on a Sprite to 0. Also, consider a second State called "Opaque" which sets the Alpha on a Sprite to 1. These two states can be combed to create values inbetween 0 and 1 for the Sprite's Alpha. This is the basics behind State Interpolation. For a more complete discussion, see this page and this page.
A visualization of the different interpolations can be found on this page.
Interpolation endpoints are the "start" and "end" of your interpolation. These endpoints are defined using states. For example, you may expose the X value on an Entity, create two states - each of which sets a different X value. Once this is done, you can interpolate between the two states. Common values used in interpolation are:
Position values such as X, Y, and Z
Alpha values for fading out/in
Rotation values
Scale values
The first step in using advanced interpolation is to enable it on a given Screen/Entity. Since the presence of advanced interpolation adds a small amount of overhead, it must be manually added to Entities/Screens which need it. To add advanced interpolation, set the "HasAdvancedInterpolation" property to true in Glue:
To assist in debugging I have created an extensible interactive input and output console logging system, very similarly to the console system made popular by the Quake games. This system allows execution of commands through a command processing engine (by default it uses javascript but any language can be easily hooked up).
With this system it is possible for developers to view debugging output and interact and update C# objects and methods from within their game. This allows developers to change AI behaviors, spawn entities, load levels, and much more.
Add a reference to the ConsoleLib dll (or project) to your project
In your Game1.cs file initialize the console management system
At the start of the Initialize() method (before all FRB initialization) add ConsoleManager.Instance.InitializeProcessor(new JavascriptConsoleProcessor());
Add some custom C# objects to be accessible via the console
In your screen's CustomInitialize method add: ConsoleManager.Instance.RegisterObjectToConsole(<c# object>, "<name of object for script referencing>", this);
In your screen's CustomDestroy() method add the following code (this is required for memory management purposes ConsoleManager.Instance.UnregisterObjectsFromSource(this);
Then in the console just type the name you registered it with and you can access all of that object's public properties and methods
To output text from code to the console system call ConsoleManager.Instance.AddOutput("My Text", "Category");
Now you can load your game up and press the ConsoleToggleKey to open and close the console window.
Built-in commands:
objects() - shows all objects registered with the console manager
properties(string) - shows all public properties for the specified object
methods(string) - shows all public methods for the specified object
With the default setup any javascript can be run, including:
function square(x) { return x * x; } - This will create a function that will square values. You can call this function by typing square(5) from the console
for loops such as for (var x = 0; x < 5; x++) { Camera.X = Camera.X + x; } (this assumes you exposed your camera to the console system.
new variables (e.g. test1 = "abcd" will create a variable called test1 that can be later called upon from the console.
Below are lower level details on how the console system works.
Endpoints are areas of code that can receive output or send input to the console. A class does not have to do both, as you can have a class that only receives output but does not send input to the console.
In order to receive output from the console you must create a method that implements the following delegate:
Once registered (coming up) this method will be called any time any output is sent to the console that the system thinks should be sent to this method (more details on that in a bit).
The first parameter is a list of strings that contain the new output in the order in which they were printed, so newText[0] is the first line added to the console and newText[1] is the line of text added next. The second parameter defines the category of the output. More about that when we describe registering this method with the console manager.
Once created your code must register this method with the console manager via the following method on the ConsoleManager class:
The first parameter is the handler method created to receive console output, with a signature matching the OutputDisplayUpdatedDelegate. The second parameter is optional, and is the name of the category of output that your handler wants to be notified of. For example, if you designate all AI output to have a category of AI, then any handler not subscribed to the AI category will not be notified of AI output. A single instance of a handler method can be registered for multiple output categories.
Observant readers will notice that even though we are subscribing a handler to a specific category, the console will call the handler with the output's category. The reason for this is because if a handler is subscribed to no category (null or empty string), then that handler will receive all output regardless of category. This allows the handler to do any special handling on text based its categorization, such as changing the color or adding a prefix based on the output's category.
Once a handler method is registered it will be immediately called once output is sent to the console. In order to stop the handler method from receiving output, the following method must be called on the ConosoleManager class:
If a category is specified then the delegate is only unregistered for that specific category. If no category is supplied then the delegate is unregistered for all categories and will no longer receive any output.
Unlike when receiving output, your code does not need to do any registration prior to sending commands to the console system for processing. All that needs to be done is making a call to ConsoleManager.Instance.ProcessInput("command text", "category"). All output that is created from processing the command will have the category passed in to the ProcessInput() call. If no category is desired the parameter can be left out (or set to an empty string)
The actual processing of commands is done through an implementation of the IConsoleProcessor interface. This abstraction allows developers to extend the console system to use any language they want, from python to Lua. All that needs to be done is to create a new class that implements IConsoleProcessor and code in all the required methods. See the Python example for how this is done.
The zip uploaded to GlueVault includes the JavascriptProcessor class, which allows console commands to utilize Javascript via the Javascript.net engine.
When making C# objects and methods accessible to the console you must make sure to keep in mind that those objects need to be removed, otherwise the console system may still hold references to the objects, preventing them from being collected by the garbage collector.
When you no longer need an object, you can remove the object from the console by calling public void UnregisterObject(string name, object obj) method on the ConsoleManager class. To quickly remove all objects from the console added by a specific class, you can use the public void UnregisterObjectsFromSource(object source), where the source object should be the same as used to register the C# objects.
The console system includes an FRB entity that can be used in your projects as a GUI to interact with the console system in the game.
In order to use the the console you must import the entity into your project via Glue. To do this:
Extract InteractiveConsole.entz from the console zip (from [GlueVault]).
Open your project in Glue
Right click on Entities and select Import Entity
Select InteractiveConsole.entz
Drag the new InteractiveConsole entity to your desired screen
Select the InteractiveConsole object in your screen and set "Attach To Camera" to true
Open the CustomActivity method in your game screen
Bind keyboard keys to console actions by setting the ConsoleToggleKey, ConsoleScrollUpKey, and ConsoleScrollDownKey properties for your InteractiveConsole instance
Load your game and press the key designated to ConsoleToggleKey and start typing into the console.
The interactive console entity has 5 main glue variables that slightly change its behavior
PercentOfScreen - This float should be a value between 0 - 1 that determines how much of the screen should be covered by the console. It defaults to 75% (0.75) of the screen.
SecondsBetweenScroll - This float controls how fast you can scroll through past console output
PixelSpacingBetweenLines - This controls how many pixels are in between each line of text in the console display. This should mostly be left at 15.
MaxStoredLines - This controls how much previous console output is kept in memory and can be scrolled through.
StartingConsoleCategory - This setting allows you to set what console category the console is subscribed to. If left blank the console will receive all console output. This is useful if you intend to only use the console for one specific purpose, such as controlling AI. This can be changed at any time in your game by calling ConsoleUI.ChangeSubscribedConsoleCategory("category name") in the console.
Another example of the use for the console system is to log data to a text file for later review. For example, if you want to run through a scenario and capture all of the AI states that occur during a turn this becomes immensely helpful. I included the following class in the ZIP for the console.
You can now log output from a specific category in two ways:
Through code by creating an instance of the class and call StartLogging with the desired category name
Add the logging class to the console, so you can start and stop logging at will via in-game console commands
Add the following code to your Game1.cs (After initializing the console's processor): ConsoleManager.Instance.RegisterObjectToConsole(this, "logger", new ConsoleOutputLogger());
Now in game, start logging by opening the console and typing logger.StartLogging("") and stop it with logger.StopLogging("")
The code on [1] contains a project for a Winforms application that can connect to your FRB game and utilize the console over the network. This will allow you to play your game full screen and issue console commands and debug from another computer if desired. It also allows you to save and load console commands from script, pause console output (for easy reading) and saving console output for later reading.
In order to enable this for your game:
Follow the install instructions to add the ConsoleLib project or dll to your FRB game
Add the following code to your Game1 constructor: _consoleServiceHost = new ConsoleServiceHost("localhost", 4000);. This will allow you to start receiving console commands with a corrosponding protected variable to your Game1 class.
Compile the ConsoleClient project.
Run your game and run the ConsoleClient
In ConsoleClient, click the Connection menu item and select Connect
Enter "localhost" and port 4000 (or whatever you instantiated the ConsoleServiceHost class with
Enter a console command and press F5 to execute it.
If Javascript is not your cup of tea, this section will show you how to extend the console system to allow for other languages. I will be using Python as an example, but the concepts here can be applied to any language.
First, download IronPython and add the libraries to your project.
Next create a new class called PythonConsoleProcessor<tt> and have it inherit from the <tt>IConsoleProcessor interface. After you visual studio's helpers to add all the required method signatures you should end up with:
Now we need to create variables to store the python engine and a constructor to initialize it, so add the following to your class:
This initializes the python engine and give us a scope to add and remove objects from. Next we need to flesh out the code to process input. Remove the NotImplementedException from the ProcessInput method and replace it with:
This will run commands through the python engine and return the result, if any. If an exception occurs the message will be sent to the console.
Note: Usually you do not want to do catch (Exception ex) as that is bad practice. However, since IronPython has a lot of exceptions that can be thrown it's hard not to. If you look at the javascript processor I am more specific with exception handling.
Now to add code to register and unregister objects with the python engine:
And that's it. We have a complete console processor that will accept and process python commands. To activate this you need to replace the current call to ConsoleManager.Instance.InitializeProcessor() with ConsoleManager.Instance.InitializeProcessor(new PythonConsoleProcessor).
Now run your game and you will have full python support.
[subpages depth="1"]
The simple command console provides an easy to use dos-style command parser featuring commands, helps, and aliases.
The simple command console is ready to go right out of the box. Simply import the entity into your project, and add it to your screen. Then use the described public methods to add new commands, helps or aliases.
This method is used to register a command. This defines the command and indicates what method should be called when the command is entered.
name is a string that represents the command to be typed. This can not contain any white space characters, as the command parser stops at the first white space when looking for a command to parse.
helpText is a string to be displayed to the screen if the user types "help name". This should describe how to use the command.
target is a delegate provided by the entity and is defined as:
The CommandTarget is the function to be called when this command is typed. If the user types anything after the command, the entirety of the line after the command is passed to the function as a single string. For instance, if the the command is "list" and the user types "list all my houses" then when the function is called it will be passed "all my houses" in the args variable.
This method allows you to register an alias for a previously registered command. An alias is another keyword that can be used instead of the commands name.
alias is a string that represents the new name of the command.
commandName is the name of the command to create an alias for.
This method allows you to register help text that is not associated with a command. This should not be used to register helps for commands, as that help should be registered with the command itself.
name is a string that the user will type after the help command to retrieve this help entry.
helpText is a string that will be displayed to the screen when the user requests this help entry.
This method allows you to register an alias for a previously registered help entry. An alias is another keyword that can be used instead of the help entry's name.
alias is a string that represents the new name of the help entry.
helpName is the name of the original help entry to create an alias for.
The console window display can be customized using the various variables that have been exposed.
X,Y : sets the bottom left corner of the console window
Z : sets the z-buffer of the display so it can show up over other sprites
NumOutputLines : The number of output lines visible in full display mode
NumCharOnLine : The number of characters allowed on each line
CursorBlinkRate : How fast the cursor should blink, smaller number is faster blink
FullDisplay : if the console is currently in full display mode
NumLinesInPartialDisplay : how many lines should be shown while in partial display mode
InputHistoryLength : how many different inputs should it remember at once
BackBufferHistoryLength : the total number of lines to be stored in the backbuffer
To issue a command in the game, type the command preceeded by a forward slash. For instance "/reset me" will call the function registered with the "reset" command, passing the argument of "me".
If you type your command without a forward slash like "reset me" the console will look to see if there is a "say" command registered, if so it will call the function registered with the "say" command and pass it the entirety of the typed input. This is the equivalent as if you typed "/say reset me". If no say command is registered, the console will tell the user that it did not recognize the command or keyword.
To get help on a topic or a command you can type "/help" followed by the topic or command that you want help on. For instance, if you wanted help on the "reset" command, you would type "/help reset", this same is true for non-command help.
If the command entered does not exist, the console will display a simple message saying that the command does not exist.
When in full mode, the console reads all input from the pc keyboard, and aside from certain special keys, all input is fed to the console display.
When in full display mode, the console watches for certain special keys that perform certain operations.
If the output display is too wide for the console's settings, it will auto-wrap the output to the last available space between characters.
The console currently only accepts input from InputManager.Keyboard
The console is currently only built for Windows PC, inputs from other devices may or may not be recognized.
Internally, there is no difference between a command help or a non-command help. Once registered all help topics are stored by keyword and display text only.
Flat Red Ball is kind of an open source oriented community. I am following suit by releasing this as open source under the MIT license. As such, you don't have very many limitations on how you can use this.
If you are really interested in reading the specific license that I've applied to this, then go here.
Did you find a bug? Do you have a suggestion for improvement? Or maybe you'd just like to let me know you are using it or provide other feedback? In any case, you can easily find me in the community or on chat as Magius96.
The RenderTargetRenderer class can be used to greatly increase the performance of FlatRedBall games which present complex scenes which never or rarely change. For static scenes a RenderTargetRenderer allows for near infinite complexity - specifically it allows for a very large number of visual elements to be drawn with almost no slowdown. Also, the RenderTargetRenderer is an easy way to render to a RenderTarget for post-processing.
This example will show how to render a very large number of Entities with essentially no slowdown. To set up the project:
Create a new Glue project
Import the following Entity: File:GraphicWithText.entz
Create a new Screen. I'll call mine GameScreen
Before we add anything to the game we'll want to turn off any framerate throttling so we can see actual performance differences. To do this open Game1.cs and add the following code to the Game1 constructor:
Next we'll add code to our GameScreen. To do this, open the GameScreen.cs file and modify the CustomInitialize and CustomActivity methods as follows:
Switch to Glue
Right-click on the Objects folder under the GameScreen
Select "Add Object"
Verify "FlatRedBall or Custom Type" is selected
Select the Sprite type
Call the Sprite RenderTargetSprite
Now we can switch to code to create a Texture2D for the Sprite. To do this, replace the CustomInitialize function with the following:
The example above shows how to perform a single render for all visuals and then to reuse that in a regular FRB Sprite for maximum performance. Of course, if the entities were being created only for a single render then we would want to destroy the entities after the render was finished. The CustomInitialize would be modified as follows:
The immediate destruction of the entities does not necessarily boost frame rate, but it does remove objects from the game which can reduce ram usage.
The ReRender function allows a render target to render itself again to the texture. The ReRender function has a number of conveniences built-in:
The initialization code does not need to be run again - the size of the texture, content managers, and name of the texture are all preserved
The Texture2D reference remains valid, so it does not need to be re-assigned
Objects do not need to be re-added to the RenderTargetRenderer's Layer - they are preserved.
The full code example for using ReRender is as follows:
The examples above show how to create entity instances and manually place them on the RenderTargetRenderer Layer. These examples illustrate how to work with the RenderTargetRenderer, but they do not reflect real Glue scenarios where objects may be defined in Glue or in files (such as Tiled TMX files) and added to Layers also created in Glue. Fortunately existing Layers can be added to RenderTargetRenderer instances. The following example code shows how to take a LayerInstance defined in Glue and to add it to the RenderTargetRenderer:
Now anything that is added to that Layer will be part of the RenderTargetRenderer. For more information see the FlatRedBall.SpriteManager.RemoveLayer and FlatRedBall.Camera.AddLayer pages.
Squid GUI is a UI framework by IONSTAR Studios. It provides functionality for a number of common controls like buttons, check boxes, labels, drop down lists, and much more. You can even expand on it by creating your own custom controls. For information regarding the Squid GUI library, please refer to their website here: http://www.ionstar.org/?page_id=4
A simple layout system has been created to make developing your GUIs as easy as possible. Keep in mind that this is fairly bare-bones and will be expanded upon over time.
You can download a sample project here: Download
This sample project includes several things:
Graphics for the controls provided by IONSTAR's SquidGUI package
An Arial10 Spritefont
A sample XML layout
Code which handles most of the internal plumbing of getting the UI to draw on the screen.
A sample "GUI Logic" entity which demonstrates how to hook your GUI interaction events back to your screen.
This sample project will let you dive right in and see how everything works. However, if you need a more hands-on tutorial please keep reading to learn how this all works.
There's essentially three steps to creating a GUI:
Create the XML layout
Create the GUI interaction logic class
Add the interaction class to your screen
This guide will take you through each of these steps with a simple example. To start, we'll create a window with an "Exit Button" inside it. When this button is clicked, a confirmation box will appear. If the user confirms, the entire application will shut down.
Each layout must contain at least one window. Components such as buttons, check boxes, and labels are contained inside of the window.
We'll use this layout for this example.
This sample layout will create a window with a button labeled "Exit". The button will be 425 pixels wide and 40 pixels tall.
When working with position, it's important to keep in mind that all controls start at the origin of its parent window. So in this case, the button will be 50 pixels down from the center of the window.
Here's a screenshot of how the screen should look:
These are the currently supported component types:
Window
Label
Textbox
Button
Checkbox
Note that you can add additional types to the SquidLayoutManager class file.
Now that we have a layout, we need to provide interaction logic to the UI. In other words, when the user clicks the "Exit Button" we want a pop-up to appear.
To start, let's look at the class called MainMenuLogic in the example project.
This class is deriving from GUIDrawableBatch, which is used for all GUI interaction classes. This abstracts all of the lower level drawing and input logic, so you can focus on making the UI actually do stuff.
Let's look at the constructor.
It's very important to note the constructor is passing "MainMenu" into the base constructor. This is actually a reference to the file name of the layout which is MainMenu.xml
Next, refer to the LoadControls() method.
Here, we're loading the window and button into memory based on the component's "Name" field which is defined in the XML layout.
The rest is fairly straightforward. The HookEvents subscribes the method ExitButton_MouseClick to the MouseClick event provided by Squid. This calls a pop-up message box.
Finally, when the pop up menu's "Yes" button is clicked, it will fire the ExitConfirmClicked method which shuts down the game.
Now that we have the interaction class created, we need to instantiate it on the screen. For the example project, we're using the screen named "DemoScreen".
This next part is super easy - check out our DemoScreen code.
And that's all there is to it! You could start doing some more complicated things like raising events from the MainMenuLogic class and responding to them from the screen. However, that is outside the scope of this guide.
Squid GUI offers a great way to develop user interfaces in FlatRedBall. You can really do a lot with its simple XML configuration and it can be expanded upon fairly easily.
Key | Operation | Mode |
---|---|---|
This results in the game running around 7 frames per second (on my hardware at the time of this writing): Notice that the entities are converted to being manually updated, which reduces the update load significantly. The low frame rate in this case comes almost purely from rendering. For more information on ConvertToManuallyUpdated, see this page. In this case we'll assume that once the Entities have been placed they will not need to move or change otherwise. We can therefore take a "snapshot" of the screen using the RenderTargetRenderer and improve the performance significantly. Since the RenderTargetRender only creates a Texture2D, we will need to create an object to display it. To do this:
The result is a much higher framerate which fluctuated into the 2000's:
Since all objects receive a full update after rotation, updates can be expensive. However once updates are complete the game resumes at full framerate:
Tilde (~)
Toggle console between Full and Mini modes
Full / Mini
Page Up
Scrolls back-buffer up one page
Full
Page Down
Scrolls back-buffer down one page
Full
Home
Scrolls all the way to the top of the back-buffer
Full
End
Scrolls all the way to the end of the back-buffer
Full
Up Arrow
Scroll up one command in the input history
Full
Down Arrow
Scroll down one command in the input history
Full