A new, clean, component-based version of FireflyGL, the C#/OpenGL 2D rendering and game engine.
Here's a quick example you can paste in your Program.Main that will display a colored square on your screen.
var stage = new Stage(800, 500, "Hello World");
stage.TreeNode.AddChild(new Entity
{
new GeometryComponent
{
new Vector2d(0, 0),
new Vector2d(100, 0),
new Vector2d(100, 100),
new Vector2d(0, 100)
},
new ShapeColorComponent
{
new Vector4(1, 0, 0, 1),
new Vector4(0, 1, 0, 1),
new Vector4(0, 0, 1, 1),
new Vector4(1, 1, 0, 1)
},
new RenderBufferComponent(stage.Renderer),
new TreeNodeComponent()
});
stage.Run();
First we make a new Stage instance. The stage is an entity to which you attach other entities that you want on the screen. TreeNode is a shorthand that points to the stage's TreeNodeComponent. Shorthands will be discussed in the next section. The TreeNodeComponent is they way you connect entities together in a hierarchical order. We supply the AddChild method with a new entity which we construct on the spot adding some components to it. The GeometryComponent provides the mesh coordinate data. It doesn't contain color information or texture coordinates so it can be used for purposes other than rendering. The ShapeColorComponent adds the color data to those points. Next, we add the RenderBufferComponent and the TreeNodeComponent. The render buffer will process the geometry and color data and update the internal buffer that is later rendered. We need the tree node so we have an "attachment point" for the stage's tree node.
After the setup is done, we call stage.Run. This blocks further code execution until we close the opened window.
Shorthands are a handy way to quickly access components in an entity. When making a new entity subclass, you can use shorthands like this
[Shorthand]
public TransformComponent Transform { get; set; }
The property will magically always have the value of the current TransformComponent attached to the entity that has it. They they have some restrictions. You can only use them on entities and you can only use them with automatic properties. They will work with normal properties as long as the getter returns the last set value. There are no restrictions to the accessibility modifier.
Now lets look at a slightly more complicated example. We'll take our entity and put it in a MakeRectangle function. Also, we'll modify it slightly.
static Entity MakeRectangle(Renderer renderer)
{
return new Entity
{
new GeometryComponent
{
new Vector2d(0, 0),
new Vector2d(200, 0),
new Vector2d(200, 100),
new Vector2d(0, 100)
},
new ShapeColorComponent
{
new Vector4(1, 0, 0, 1),
new Vector4(0, 1, 0, 1),
new Vector4(0, 0, 1, 1),
new Vector4(1, 1, 0, 1)
},
new RenderBufferComponent(renderer),
new TreeNodeComponent(),
new TransformComponent()
};
We elongated the rectangle, and we added a new component. The TransformComponent, like it's name says, handles transformations. That's stuff like X, Y, Rotation and Scale. So let's make a few rectangles and link them together.
var stage = new Stage(800, 500, "Hello World");
var a = MakeRectangle(stage.Renderer);
var b = MakeRectangle(stage.Renderer);
var c = MakeRectangle(stage.Renderer);
a.GetComponent<TreeNodeComponent>().AddChild(b);
b.GetComponent<TreeNodeComponent>().AddChild(c);
b.GetComponent<TransformComponent>().X = 200;
c.GetComponent<TransformComponent>().X = 200;
a.GetComponent<TransformComponent>().ScaleX = 0.5;
a.GetComponent<TransformComponent>().ScaleY = 0.5;
stage.TreeNode.AddChild(a);
stage.Run();
So let's see here. We make 3 rectangles, link them a -> b -> c and then modify some of their properties. We put b and c at X = 200. We also shrink a by half. Then we add a to stage. What we see when we run the program are all three rectangles because you don't need to add each of them separately to the stage. You need to add the root of the tree you want displayed. Secondly, the first rectangle isn't the only one scaled. They are all the same size. Why? Because changing the transformation of the parent entity changes the coordinate system of the child entity. This also means that b and c are not in the same spot because b is shifter 200 to the right RELATIVE TO a and c is shifter 200 to the right RELATIVE TO b. In fact, they are only shifted by 100 each since the whole coordinate system is scaled to one half.
If we were to rotate a, the whole stick of rectangles would rotate.
One extra thing to note here is that we need to use GetComponent instead of just TreeNode since we didn't (and can't) define a shorthand. This is because we made a new entity on the spot instead of making a new class that inherits from entity. Since writing GetComponent<...> gets real boring real fast, shorthands were introduced.
Next, let's take a look at mouse interaction. The idea is simple. We want to know if an object if being hovered over with the mouse. If it is, make it grow slightly. This is how it should look.
static Entity MakeRectangle(Renderer renderer)
{
return new Entity
{
new GeometryComponent
{
new Vector2d(0, 0),
new Vector2d(200, 0),
new Vector2d(200, 100),
new Vector2d(0, 100)
},
new ShapeColorComponent
{
new Vector4(1, 0, 0, 1),
new Vector4(0, 1, 0, 1),
new Vector4(0, 0, 1, 1),
new Vector4(1, 1, 0, 1)
},
new RenderBufferComponent(renderer),
new TreeNodeComponent(),
new TransformComponent(renderer),
new MouseInteractionComponent()
};
}
var stage = new Stage(800, 500, "Hello World");
var a = MakeRectangle(stage.Renderer);
var b = MakeRectangle(stage.Renderer);
var c = MakeRectangle(stage.Renderer);
b.GetComponent<TransformComponent>().X = 100;
c.GetComponent<TransformComponent>().X = 200;
ge.Update.Update += delegate
{
new List<Entity>() { a, b, c }.ForEach(ent =>
{
var intersects = ent.GetComponent<MouseInteractionComponent>().IntersectsMouse;
if (intersects)
{
ent.GetComponent<TransformComponent>().ScaleX = 0.6;
ent.GetComponent<TransformComponent>().ScaleY = 0.6;
}
else
{
ent.GetComponent<TransformComponent>().ScaleX = 0.5;
ent.GetComponent<TransformComponent>().ScaleY = 0.5;
}
});
};
stage.TreeNode.AddChild(a);
stage.TreeNode.AddChild(b);
stage.TreeNode.AddChild(c);
stage.Run();
Ok. So there are a lot of interesting things going on here. Firstly, let's look at our MakeRectangle function. It adds a MouseInteractionComponent to our entities. The setup is similar to the last section except this time the entities aren't linked and they are each added separately to the stage. Then, we hook onto the stage's update event. Stage has an Update shorthand that points to it's UpdateComponent. That component has an Update event and an AfterUpdate event that you can add your listeners too. On each update, for each rectangle we check if it intersects the coordinates of the mouse.
This is not a finished feature. The final version will not require so much under-the-hood work.
If the mouse intersects the object, we scale it. If it doesn't, we keep the scale at 0.5.
Having trouble with Firefly2? You can email me at lukahorvat9@gmail.com