I moved to a new URL! Check it out!

Otter Surface Example

Otter Surface Example
Just a quick post going over how I usually use Surfaces in Otter. A surface is basically a render texture, or render target. The idea of calling it a surface mostly comes from Game Maker.

By default Otter will render all of the graphics to the main surface. However you can make your own surfaces and use them in various ways. The main point of this quick example is to show how to separate HUD elements and gameplay onto different surfaces. The big advantage of doing this is you can now freely rotate and zoom the camera and the HUD will be unaffected.

Alright let's check out the codes!
class Program {
static void Main(string[] args) {
// Create a new game to play.
var game = new Game();

// Set the scroll of the surfaces to 0 so they never scroll on the camera.
Global.SurfaceGameplay.Scroll = 0;
Global.SurfaceHud.Scroll = 0;

// Start the game with a new GameScene.
game.Start(new GameScene(1000, 1000));

}
}

class GridBackground : Entity {
public GridBackground() : base() {
Layer = 10;
Surface = Global.SurfaceGameplay;
}

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

// Just render a simple grid background so when the camera scrolls we can see it.
AddGraphic(new Grid(Scene.Width, Scene.Height, 40, 40, Color.Cyan));
Graphic.Alpha = 0.4f;
}
}

class Player : Entity {
public Player(float x, float y) : base(x, y) {
// Add a basic red box for the graphic.
AddGraphic(Image.CreateRectangle(20, 20, Color.Red));
Graphic.CenterOrigin();

// Assign this entity to render to Global.SurfaceGameplay instead of the default game surface.
Surface = Global.SurfaceGameplay;

// Set up a collider for movement.
var collider = AddCollider(new BoxCollider(20, 20));

// Set up the rest of the movement stuff.
var axis = AddComponent(Axis.CreateWASD());
var movement = AddComponent(new BasicMovement(400, 400, 30));
movement.Axis = axis;
movement.Collider = collider;
}
}

class PlayerHud : Entity {
public PlayerHud() : base() {
// Basic hud test with just some text.
AddGraphic(new Text("This is the HUD!", 20));

Surface = Global.SurfaceHud;
}
}

class GameScene : Scene {
public GameScene(int width, int height) : base(width, height) {
// Add the entities to the scene when the scene is created.
Add(new Player(HalfWidth, HalfHeight));
Add(new GridBackground());
Add(new PlayerHud());
}

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

// Set the scene's camera to follow the player.
var player = GetEntity<Player>();
CenterCamera(player.X, player.Y);

// Set the surface's camera to follow the scene's camera.
Global.SurfaceGameplay.CameraX = CameraX;
Global.SurfaceGameplay.CameraY = CameraY;

// Zoom the surface up and down with the arrow keys.
if (Input.KeyDown(Key.Down)) {
Global.SurfaceGameplay.CameraZoom -= 0.005f;
}
if (Input.KeyDown(Key.Up)) {
Global.SurfaceGameplay.CameraZoom += 0.005f;
}
}

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

// Render the surfaces.
Draw.Graphic(Global.SurfaceGameplay);
Draw.Graphic(Global.SurfaceHud);
}
}

class Global {
// Keep global references to the surfaces used for rendering the game.
public static Surface SurfaceHud = new Surface(640, 480);
public static Surface SurfaceGameplay = new Surface(640, 480, Color.Gray);
}
The full version is available on pastie.

I have a simple player class, and background class which both render to the surface designated for gameplay. Then the player HUD class is assigned to render to the hud surface. The scene will then take care of rendering both of those surfaces to the game's main surface. I render in the scene's Render method because that will be the very last thing that renders in the frame.

I know some people have been having trouble understanding surfaces fully and how to use them so I hope this example helps. Surfaces can be really powerful especially when you start getting into shaders and one of my main goals for Otter was to make using surfaces as painless as possible.
new comment!

Post your comment!

Name
Email
Comment