I moved to a new URL! Check it out!

posts filed under: general

Dev Log: Shader Experiments

Dev Log: Shader Experiments
Since my break to work on the remaining bits of Otter I haven't really fallen back into the groove on working on my big game project yet. Yesterday I dedicated the day to playing around with shaders since I'm really interested in using them for all kinds of cool visual effects.

Image


This is what I ended up coming up with at the end of the day. First I was playing around with gradient maps for recoloring an image, and then I started to mess with displacement to make cool waves in the image. Finally I wanted to experiment with having a render texture that could be passed to the shader for dynamic effects.

Basically how this little program works is that there is the static image with a shader loaded. Then there's a Surface object that can be painted on with the mouse. The shader uses the texture from that Surface to affect the image in different ways.

Here's the full C# source:
using Otter;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ShaderTesting {
class Program {
static void Main(string[] args) {
var image = new Image("pic.jpg");
var game = new Game("ShaderZ", image.Width, image.Height);

game.FirstScene = new TestScene();

game.Start();
}
}

class TestScene : Scene {

Image image = new Image("pic.jpg");
Image circle = new Image("circle.png");
Surface distortion;

public TestScene() {
AddGraphic(image);
distortion = new Surface(image.Width, image.Height);
circle.CenterOrigin();
circle.Blend = BlendMode.Add;
AddGraphic(distortion);
circle.Alpha = 0.1f;
distortion.Visible = false;
distortion.AutoClear = false;
}

public override void Update() {
base.Update();

if (Timer % 30 == 0) {
try {
image.Shader = new Shader("../../shader.frag");
}
catch(Exception e) {
Console.WriteLine("Shader error:");
Console.WriteLine(e.Message);
}
image.Shader.SetParameter("gradient", new Texture("gradient.png"));
}
if (Input.MouseButtonPressed(MouseButton.Right)) {
distortion.Clear();
}
image.Shader.SetParameter("time", Timer);
image.Shader.SetParameter("distortion", distortion.Texture);
image.Shader.SetParameter("offset", Util.SinScale(Timer, 0, 1));

if (Input.MouseButtonDown(MouseButton.Left)) {
Draw.SetTarget(distortion);
Draw.Graphic(circle, MouseX, image.Height - MouseY);
Draw.ResetTarget();
}
}

public override void Render() {
base.Render();

Draw.Circle(MouseX - 15, MouseY - 15, 30, Color.None, Color.Red, 2);
}
}
}

And the GLSL shader source:
sampler2D texture;
sampler2D gradient;
sampler2D distortion;
uniform float offset;
uniform float time;
uniform float mixfactor;

void main() {
vec2 pos = gl_TexCoord[0];
vec2 distortColor = texture2D(distortion, pos);
pos.x += distortColor.r * (sin(time * 0.1 + pos.x * 10) * 0.05);
pos.y -= distortColor.g * (cos(time * 0.1 + pos.y * 10) * 0.05);
vec4 color = texture2D(texture, pos);

float gray = (color.r + color.g + color.b) / 3;

vec4 gradientColor = texture2D(gradient, vec2(gray, offset));

gl_FragColor = mix(color, gradientColor, distortColor.r + distortColor.g);
}

You can download everything here. Note that you'll have to give it a reference to Otter to compile and run it yourself.

Dev Log: To Batch or not to Batch

Dev Log: To Batch or not to Batch
One of the things gnawing at me over the past few months is the rendering performance of Otter. So far it hasn't really been a problem, but I've only made a couple of low resolution prototypes and a game jam game with it so far. For my current project, a 1920 x 1080 resolution game, I started to worry about the rendering time as I add more and more assets to it.

So I was thinking "Hey maybe I can add some kinda sprite batching to Otter like XNA or Monogame has!" The basic concept is that it is very expensive to switch textures when rendering things with a GPU, so you take a bunch of sprites that all share the same texture and try to mash them together into one draw call from the video card. This way you can render all your sprites but you don't have to switch textures for every single one of them.

To start on this I basically tore down a bunch of rendering code in Otter. This was a few days ago, and since then I do have some sort of basic sprite batch working. In a little example I have 1000 sprites rendering with the same texture and as far as the video card knows it's actually only 2 renders... but so far I actually don't think this has affected performance at all!

I'm using SFML.Net for the core rendering of Otter. Everything that goes to the video card to show up on the screen goes through SFML's Draw functions. So far according to my small tests it seems that the video card isn't really having any issue switching textures a thousand times, but something before that is actually causing the slow down.

It seems that just going through and appending vertices to a vertex array in SFML.Net is just really slow. For example Otter's RichText class suffers from a pretty big performance hit if it's used to render a lot of text, like enough letters to cover the entire screen. This is pretty odd since RichText.cs in Otter is pretty much a copy of SFML's own text class from the C++ source! Almost the same code in C# runs almost 10x slower than C++, and of course this is not a big surprise since C# has a bunch of extra stuff with the managed memory and garbage collection. Still, it is pretty discouraging to realize. (Another tough one is the Tilemap class, which will suffer from big slow down during real time modification of a huge tilemap due to the rebuilding of the VertexArray!)

So right now I'm still reorganizing a bunch of code in Otter's rendering stuff. Hopefully the API will remain mostly unchanged, but right now my hope is that using texture atlases in Otter becomes smoother and actually has a performance benefit. Yay programming.

Dev Log: Using Config Objects

Dev Log: Using Config Objects
One of the recent changes in Otter is the ability to configure the RichText object with a RichTextConfig object. This was inspired by something I saw some friends of mine doing in their project. Instead of having a long drawn out list of possible parameters for the creation of an object, instead you can pass in a config object that has all of those parameters set. Here's a quick example.
//In a static class that holds all the Config objects
public static RichTextConfig RTSkyCellStat = new RichTextConfig() {
ShadowColor = G.Colors.Dark,
OutlineColor = G.Colors.Dark,
OutlineThickness = 3,
CharColor = G.Colors.Light,
ShadowY = 3,
Font = Assets.FontHud,
MonospaceWidth = 16,
FontSize = 28
};

And creating the RichText object with this looks like this:
public RichText TextHealth = new RichText("600", Config.RTSkyCellStat);

One of the coolest things about using a Config object is that one Config object can be used for a bunch of different RichText objects. This means that I can define common styles of RichText in a static class somewhere and reuse them as much as I want, so if I need to change something across all of those RichText objects all I need to do is change the config.

I'm not sure if this is the best method of doing this in C# land but so far it seems to be working pretty well. I'm also using Configs for a lot of my UI elements as it lets me define a couple of different styles for panels and buttons and reuse them for elements that share similar roles.

Dev Log: Menus

Dev Log: Menus
I've been a little bit in a heads down mode over the past couple days while working on my video game. I've been posting some updates over on my Twitter in the form of little screenshots and animated gifs, but haven't really written about anything in awhile!

One of the big hurdles to overcome to get this game into a real, actual playable state is making a good menu system. The game is going to be relying a lot on menus for the player to build things in the world, change their equipment, check their status, and who knows what else. I tried looking into some menu frameworks, but I've fallen back onto just making my own menu classes and hard coding everything.

Image


Everything you see here is just made up of two classes: Menu, and MenuButton. Menu can have a list of child Menus, and a list of MenuButtons. MenuButtons have callbacks and such for when they are clicked, rolled over, rolled off, and other handy things. Since everything in this game is controlled by a cursor of some sort, the menu's are a little bit easier to design. Also for now all I have to worry about are buttons. I haven't gone down the path of text input or scroll bars or selection lists, so for now just two little classes will suffice.

Almost all the art in the menus are also made up of NineSlice graphic objects in Otter. NineSlice panels make it really easy to design UI, since the same texture can be used for any size panel. For more info on how NineSlices work, check this out.

That's it for now. Hoping to get a nice logo and officially say what the name of this game is soon (although I've been tagging these posts with the name of it for a few weeks now I think.)

Dev Log: Boulder Juice

Image


Lately I've been getting a lot done on this game! Actually I'm not totally sure if that's true, but it feels like I've been getting a lot done. I've started to do more organized work blocks which have been working out nicely so far. (I might've mentioned this before.)

While I work away at menus and UI I took a break to work on some good ole fashioned particle effects. Right now the game has boulders that can be harvested for materials to build islands out of. No idea if this is going to be in the final game, but it's fun to blow them up!

Dev Log: Enemy Waves

Dev Log: Enemy Waves
Waves of enemies can be a pretty confusing thing to manage. It sounds simple at first, but the amount of ways that you can manage a group of enemies entering the game seems nearly endless. Usually in games where I have waves of enemies I don't have enough time to think of an elegant solution. I just usually end up with some sort of random enemy generator and over time I add more potential enemy types to the pool. This works most of the time but can often lead to really weird patterns, or nearly impossible to survive scenarios if its not tuned correctly.

For this game I have a little bit more time than a weekend to complete it, so this gives me an opportunity to try and come up with a cool solution. For the current design of the game there exist enemy spawner tiles in the game world. These tiles are present from the very start of the session, although all of them might not be active yet.

Enemies can spawn from any one of the active tiles in the scene. There is always at least one active enemy spawner from the start. An enemy wave consists of a list of enemies, and each one of those list entries also knows what enemy spawner they should come from, and how long they should wait before appearing.

When I have a wave ready to go I can add it into my EncounterManager object which will then execute each wave. An enemy wave can either wait for a certain amount of time before spawning the next one, or it can wait for all of the enemies to be cleared before spawning the next one.

So with all of that, this is what it currently looks like to set up enemy waves:
var wave2 = new EnemyWaveProperties()
.Add(typeof(EnemyBitty))
.SpawnFrom(1)
.Add(typeof(EnemyBitty))
.SpawnFrom(0)
.Delay(120)
.Add(typeof(EnemyPopcorn))
.SpawnFrom(1)
.Add(typeof(EnemyPopcorn))
.Delay(120)
.SpawnFrom(0)
.Add(typeof(EnemyBitty))
.Add(typeof(EnemyBitty))
.Add(typeof(EnemyBitty));

EncounterManager.AddAction(new EncounterActionEnemyWave(wave2));
And here's a quick look at what it looks like when the encounter manager is executing an enemy wave action.
public override void Execute() {
foreach(var wave in properties.Waves) {
var spawner = EnemySpawner.GetSpawnerById(wave.SpawnerId);

foreach (var waveEnemy in wave.Enemies) {
if (Spawning) {
if (Timer == waveEnemy.SpawnDelay) {
var enemy = spawner.Spawn(waveEnemy.EnemyType);
EnemiesSpawned.Add(enemy);
SpawnedFirstEnemy = true;
}
}
}
}

if (Timer >= properties.MaxSpawnDelay) {
Spawning = false;
}

if (WaitForClear && SpawnedFirstEnemy) {
var enemiesDead = true;
foreach (var enemy in EnemiesSpawned) {
if (enemy.IsInScene) { // Enemy is still alive
enemiesDead = false;
}
}
if (enemiesDead) {
Finished = true;
EnemiesSpawned.Clear();
}
}
else {
if (Timer >= Time) {
Finished = true;
}
}

Timer++;
}
Nothing too crazy, I think!