Introduction
Welcome to the tophat documentation. This documentation contains some guides for using tophat and reference for tophat's Umka API.
What is tophat?
Tophat is a 2d game library for the Umka programming language. It is small and simple to use. You can visit the homepage at tophat2d.dev.
Getting started
This chapter contains guides for getting started with tophat.
Installation
The first step on the road to using tophat is to install it. If you download the prebuilt binaries, it is a very simple process.
Installing tophat on Linux
All commands are prefixed either using
$
or#
. The former is used for commands that can be run as a normal user, latter is used for commands requiring root privileges.
First you have to download tophat from this page. You
can do that manually or using this curl
command.
$ curl https://tophat2d.dev/dl/tophat-linux -o tophat
When you successfully download the binary, the next step is to install it to a
directory, which is in the system path. For example /usr/bin
.
# install tophat /usr/bin
To check if tophat was installed correctly, run tophat version
.
Installing tophat on Windows
Download the tophat exe from the downloads page. Then move it to a folder of your liking. Whenever you want to make a tophat project, it is recommended to make a shortcut to tophat in the project directory. This way you won't have to use the file dialog every time you run your game.
Running the examples
To run the examples, clone the tophat repository.
The examples are in the examples/
folder. On linux, cd to an example you
want to run and type tophat
. On windows, launch tophat and choose the folder
of the example you want to run.
Text editors with umka support
No text editors support umka out-of-the-box, but there are available extensions for the most popular ones.
Hello Tophat!
This chapter shows you how to make a simple hello world program in tophat.
Firstly make a new folder. In that folder create a file called main.um
, open
it in your favorite text editor and write the following in:
import (
"canvas.um"
"th.um"
"window.um"
)
fn init*() {
window::setup("Hello tophat!", 600, 600)
window::onFrame.register({
canvas::drawText("Hello tophat!", { 1, 1 }, th::black, 2)
})
}
Now run the file. On linux you would cd
into the directory and run tophat
.
On windows, open tophat
and choose the directory or make a shortcut to tophat
in the folder, so you can run the game by just double-clicking it.
Does everything work? Great! Is there a problem? Contact me at marek@mrms.cz.
Now let's explain all the parts of the example.
import (
"canvas.um"
"th.um"
"window.um"
)
The import statement imports all the modules you will need. The strings in the
import
statements are file-paths, but all the modules included in the example
are tophat builtin modules. This means you can import them even though they
aren't on the file system. All modules have their documentation available
here.
fn init*() {
This declares the init
function and exports it. This function your game's
entry point.
window::setup("Hello tophat!", 600, 600)
This function call creates a window. It will set the title to "Hello tophat!" and the dimensions to 600x600.
window::onFrame.register({
This registers a callback to the onFrame
signal. What this means is that the
code in the brackets will be called on every frame. This is you game's main
loop.
canvas::drawText("Hello tophat!", { 1, 1 }, th::black, 2)
This function draws the text Hello tophat!
at position 1, 1
, with black
color and scaled 2 times.
Tophat tips
The API reference is your best friend.
Rotations are in degrees.
Assets paths are prefixed with the
dir
. That is./
by default, but you can specify it using thedir
flag. If you prefix a path withraw://
, it will not be changed by tophat.
You can get last frame length from
th.delta
. This is useful for making your game FPS independant. A good rule of thumb is to multiply any kind of movement by theth.delta
value.
Resizing the window won't change how the game is displayed - the viewport stays the same. The viewport size is in the
window.wp
variable. You can change it to your liking.
Dimensions of one
canvas.drawText
character is 5x5 px, and the character spacing is 1px.
input.getMouseDelta
works even if the cursor is frozen usingwindow.freezeCursor
Umka API reference
Documentation for various builtin tophat modules. This documentation
is automatically rebuilt upon every push to the main
branch.
struct Anim
type Anim* = struct {
// the source atlas
atl: atlas::Atlas
// the first cell of the animation
min: int
// the last cell of the animation
max: int
fps: real32
// offset in time
offset: int
}
Anim allows you to animate between individual frames of an atlas.
fn mk
fn mk*(atl: atlas::Atlas, fps: int, min: int = 0, max: int = -1 /* len(atl) - 1 */, offset: int = -1 /* th.time */): Anim {
Anim
constructor
fn Anim.animate
fn (anm: ^Anim) animate*(time: int) {
Crops the base atlas to the cell that should be visible at time
.
fn Anim.framesPlayed
fn (anm: ^Anim) framesPlayed*(time: int): int {
Returns how many frames were played at time
.
struct Atlas
type Atlas* = struct {
i: image::Image // source image
cs: th::Vf2 // size of a cell in pixels
dm: th::Vf2 // amount of cells in image
}
Atlas is an image containing tiles in a square grid.
fn mk
fn mk*(i: image::Image, dm: th::Vf2): Atlas {
i: source image dm: amount of cells
fn mk2
fn mk2*(i: image::Image, cs: th::Vf2): Atlas {
i: source image cs: size of a cell in pixels
fn Atlas.coords
fn (a: ^Atlas) coords*(n: int): th::Vf2 {
returns the coordinates of the nth tile
fn Atlas.rect
fn (a: ^Atlas) rect*(n: int): rect::Rect {
returns the rectangle of the nth tile
fn Atlas.index
fn (a: ^Atlas) index*(at: th::Vf2): int {
returns the index of the tile at the given coordinates
fn Atlas.has
fn (a: ^Atlas) has*(at: th::Vf2): bool {
returns true if the tile at the given coordinates exists
fn Atlas.hasIndex
fn (a: ^Atlas) hasIndex*(n: int): bool {
returns true if the tile at the given index exists
fn Atlas.cropSource
fn (a: ^Atlas) cropSource*(at: th::Vf2) {
Crops the sourse image to only show a wanted tile
enum PackStrategy
type PackStrategy = enum {
square
row
column
}
fn pack
fn pack*(images: []image::Image, strategy: PackStrategy): (Atlas, std::Err) {
Packs an array of images into an atlas
fn Atlas.draw
fn (a: ^Atlas) draw*(at: th::Vf2, t: th::Transform, tint: uint32 = 0xFFFFFFFF) {
Draws the tile at at
Module for audio loading and playback.
opaque Sound
type Sound* = struct { _: ^struct{} }
Represents an instance of a playable sound. It is an opaque structure.
enum LoadFlag
type LoadFlag* = enum (uint32) {
none = 0
// Streams the audio, only saving 2 seconds into memory.
stream = 1
// Loads the sound in async. Use `audio.waitForLoad` to wait for the
// sounds to load.
async = 4
}
fn load
fn load*(path: str, flags: LoadFlag = LoadFlag.none): (Sound, std::Err) {
Loads a sounds at path, if there is an error, the underlying pointer
will be NULL
and validate
will return false.
fn Sound.validate
fn (s: ^Sound) validate*(): bool {
Returns true
if s
loaded correctly.
fn Sound.copy
fn (s: ^Sound) copy*(): (Sound, std::Err) {
Copies the sound. This will create another sound which can be configured and played independently from the original sound.
fn Sound.isPlaying
fn (s: ^Sound) isPlaying*(): bool {
Returns true if the sound is still playing.
fn Sound.play
fn (s: ^Sound) play*() {
Plays the sound.
fn Sound.start
fn (s: ^Sound) start*(): (^Sound, std::Err) {
The start function allows you to play a single sound multiple times. It will create a copy and return a pointer to it, so you can controll it while it is playing. The returned pointer can be discarded.
fn Sound.stop
fn (s: ^Sound) stop*() {
Stops the sound, but keeps the progress. If you want to start from the
begginning, use audio.Sound.seekToFrame(0)
.
fn Sound.setVol
fn (s: ^Sound) setVol*(vol: real32) {
Sets the volume as a multiplier of the base volume.
fn Sound.setPan
fn (s: ^Sound) setPan*(pan: real32) {
Sets the pan of the sound.
fn Sound.setPitch
fn (s: ^Sound) setPitch*(pitch: real32) {
Sets the pitch of the sound.
fn Sound.setLooping
fn (s: ^Sound) setLooping*(looping: bool) {
Sets whether the sound will loop upon finishing.
fn Sound.seekToFrame
fn (s: ^Sound) seekToFrame*(frame: uint) {
Seeks to a specified PCM frame.
fn Sound.frameCount
fn (s: ^Sound) frameCount*(): uint {
Returns length of the sound in PCM frames.
fn Sound.lengthMs
fn (s: ^Sound) lengthMs*(): uint {
Returns length of the sound in ms.
fn Sound.setStartTimeMs
fn (s: ^Sound) setStartTimeMs*(t: uint) {
fn Sound.setStopTimeMs
fn (s: ^Sound) setStopTimeMs*(t: uint) {
Canvas library allowing for drawing basic shapes. Coordinates are based on the screen.
fn drawText
fn drawText*(text: str, pos: th::Vf2, color: uint32, size: th::fu) {
Draws a basic pixel text. Only ascii is supported.
fn textSize
fn textSize*(text: str, scale: th::fu): th::Vf2 {
Returns the size of text taken by an equivalent drawText call.
Pixel Font
var pixelFont*: PixelFont
The pixelFont
variable exposes the canvas pixel font as a generic font.
fn drawRect
fn drawRect*(color: uint32, r: rect::Rect) {
Draws a Rectangle.
fn drawLine
fn drawLine*(color: uint32, b, e: th::Vf2, thickness: th::fu) {
Draws a line.
fn drawRectLines
fn drawRectLines*(color: uint32, r: rect::Rect, thickness: real32 = 1.0) {
Draws rect border.
fn drawQuad
fn drawQuad*(color: uint32, q: th::Quad) {
Draws a convex quad.
fn drawTrig
fn drawTrig*(color: uint32, a, b, c: th::Vf2) {
Draws a triangle.
fn beginScissorRect
fn beginScissorRect*(r: rect::Rect, debug: bool = false) {
Disable rendering outside of rect r
fn endScissor
fn endScissor*() {
Stops cropping
Builtin collision functions. The ic argument stores the collision position.
fn lineToLine
fn lineToLine*(b1, e1, b2, e2: th::Vf2, ic: ^th::Vf2): bool {
Checks for a collision between 2 lines specified by their end points.
fn vf2ToQuad
fn vf2ToQuad*(p: th::Vf2, q: th::Quad, ic: ^th::Vf2): bool {
Checks for a collision between a vf2 and a quad.
fn lineToQuad
fn lineToQuad*(b, e: th::Vf2, q: th::Quad, ic1, ic2: ^th::Vf2): bool {
Check for a collision between a line and quad edges.
fn quadToQuad
fn quadToQuad*(q1, q2: th::Quad, ic: ^th::Vf2): bool {
Check for a collision between two quads.
fn vf2ToRect
fn vf2ToRect*(p: th::Vf2, r: rect::Rect): bool {
Check for a collision between a vf2 and a rectangle.
fn rectToRect
fn rectToRect*(r1, r2: rect::Rect): bool {
Check for a collision between two rects
fn rectIntersect
fn rectIntersect*(r1, r2: rect::Rect): rect::Rect {
Get the intersection of two rects.
Color operations. Operate on RGBA uint32 values.
fn hsv
fn hsv*(h, s, v: th::fu, a: th::fu = 1.0): uint32 {
Converts HSV values into RGBA uint32 color. NOTE: Hue is between 0 and 1
fn alpha
fn alpha*(c: uint32, to: th::fu): uint32 {
Sets alpha of the color c to a value in to.
fn rgb
fn rgb*(r, g, b: th::fu, a: th::fu = 1.0): uint32 {
Constructs RGBA uint32 from RGBA of reals.
A CSV parser, which also works for similar formats. It doesn't support quotes, but you can escape characters using a backslash.
fn parse
fn parse*(inp: str, sep: char = ','): [][]str {
Parses input into a 2d string array.
fn encode
fn encode*(inp: [][]str, sep: char = ','): str {
Converts 2d array to csv string.
struct Ent
type Ent* = struct {
// used as a collider, used as backup when invalid image is supplied
r: rect::Rect
// used in drawing
i: image::Image
// used to transform and translate the image and rect
t: th::Transform
// used as a color of the rect and a color filter for the image
c: uint32
}
Entity is the main game object. It features drawing and collisions. Every entity has an image used for drawing and a rectangle used for collisions. It also has a transform used for transforming it's image and rectangle.
struct Coll
type Coll* = struct {
index: th::uu
pos: th::Vf2
}
Value returned by get coll. It contains a position where the collision happened and the index of the entity involved in said collision.
fn Ent.draw
fn (e: ^Ent) draw*() {
Draws the entity onto the screen.
fn mk
fn mk*(img: image::Image = image::Image{}, t: th::Transform = th::Transform{ s: {1, 1} }): Ent {
ent's constructor
fn Ent.getColl
fn (e: ^Ent) getColl*(s: []^Ent, maxColls: th::uu): []Coll {
Checks collisions of e with entities in s. Checks at max maxColl collisions. If s contains e, the collision won't be returned.
fn Ent.animate
fn (e: ^Ent) animate*(fps: int, frames: ^[]image::Image, t: int) {
Animates the entity's image with one of the anim
array. Won't begin on
the first frame. If you want that, use anim.Anim.
Module for font rendering. Unicode is supported, but only left to right.
interface Font
type Font* = interface {
// Draw text to the viewport
draw(text: str, pos: th::Vf2, color: uint32, scale: th::fu = 1.0)
// Return the dimensions of the text at scale 1
measure(text: str): th::Vf2
// Return true if the font is valid
validate(): bool
}
A generic interface for fonts. Fonts can be loaded from file using
font::load
, or a built in canvas can be used from canvas::pixelFont
.
fn load
fn load*(path: str, size: th::fu, filter: image::Filter = .linear): (Font, std::Err) {
Loads a font from a path and returns it.
enum Filter
type Filter* = enum {
nearest = 0
linear = 1
}
Filtering mode for images, render targets, and fonts.
opaque Image
type Image* = struct{ _: ^struct{} }
Represents a drawable image. It is an opaque structure. Images support a color filter. It is applied by multiplying the color of each pixel with the filter.
fn load
fn load*(path: str): (Image, std::Err) {
Loads an image at path.
fn Image.validate
fn (i: ^Image) validate*(): bool {
Returns true, if i's handle points to an image.
fn Image.flipv
fn (i: ^Image) flipv*(flip: bool) {
Flips image on it's vertical axis.
fn Image.fliph
fn (i: ^Image) fliph*(flip: bool) {
Flips image on it's horizontal axis.
fn Image.draw
fn (i: ^Image) draw*(t: th::Transform, color: uint32 = th::white) {
Draws the image in screen coordinates. It transforms it with t and applies color as a color filter.
fn Image.blit
fn (i: ^Image) blit*(src, dest: rect::Rect, color: uint32 = 0xFFFFFFFF, rot: th::fu = 0, origin: th::Vf2 = {0, 0}) {
Copies source rectangle (texture coordinates) to destination rectangle (screen coordinates).
fn Image.drawNinepatch
fn (i: ^Image) drawNinepatch*(outer, inner, dest: rect::Rect, color: uint32 = th::white, scale: real = 1.0) {
Draws "nine-patch" image.
outer
specifies the texture coordinates of the outer rect of source image,
inner
specifies coordinates of inner rect of source image, positioned relative to outer
.
You can tint with color
.
fn Image.drawOnQuad
fn (i: ^Image) drawOnQuad*(q: th::Quad, color: uint32 = th::white) {
Draws the image on top of a quad with corners of the image positioned on the verticies of the quad.
fn Image.getDims
fn (i: ^Image) getDims*(): th::Vf2 {
Returns width and heigth.
fn Image.crop
fn (i: ^Image) crop*(tl, br: th::Vf2) {
Crops an image. Coordinates are between 0, 0 (top left) and 1, 1 (bottom right)
fn Image.cropPx
fn (i: ^Image) cropPx*(tr, br: th::Vf2) {
Same as Image.crop
, but the positions are in pixels.
fn Image.cropRect
fn (i: ^Image) cropRect*(r: rect::Rect) {
Same as Image.crop
, but uses a rect instead of two positions.
fn Image.cropQuad
fn (i: ^Image) cropQuad*(q: th::Quad) {
Crop an image using a quad.
fn Image.getCropQuad
fn (i: ^Image) getCropQuad*(): th::Quad {
Crop an image using a quad.
fn mk
fn mk*(data: []uint32, dm: th::Vf2): (Image, std::Err) {
Creates an image from raw data.
fn Image.copy
fn (i: ^Image) copy*(): (Image, std::Err) {
Copies image into a new one.
fn Image.setfilter
fn (i: ^Image) setfilter*(filter: Filter): std::Err {
Sets a mag/min filter. 0 is nearest, others are linear. This function will regenerate the texture. This means it shouldn't be used in a loop. https://learnopengl.com/img/getting-started/texture_filtering.png left is nearest, right is linear.
fn Image.setData
fn (i: ^Image) setData*(data: []uint32, dm: th::Vf2): std::Err {
Updates the image data. dm are the dimensions of the new image. The new image doesn't have to be the same size as the old one.
fn Image.getData
fn (i: ^Image) getData*(): []uint32 {
Gets the image data. This downloads the data from the GPU on each call. Don't use in performance critical sections.
Module for getting keyboard and mouse input. is* functions return info based on a us QWERTY layout. They are supposed to be used for game controls. For text input use getStr.
Keycode constants
type Key* = enum {
mouse1 = 1 // NOTE: mouse 2 and 3 key codes are swapped
mouse2 = 3 // because sokol uses 3 for middle mouse
mouse3 = 2 // button.
ctrl = 16
shift = 17
alt = 18
space = 32
apostrophe = 39 /* ' */
comma = 44 /* , */
minus = 45 /* - */
dot = 46 /* . */
slash = 47 /* / */
num0 = 48
num1 = 49
num2 = 50
num3 = 51
num4 = 52
num5 = 53
num6 = 54
num7 = 55
num8 = 56
num9 = 57
semicolon = 59 /* ; */
equal = 61 /* = */
a = 65
b = 66
c = 67
d = 68
e = 69
f = 70
g = 71
h = 72
i = 73
j = 74
k = 75
l = 76
m = 77
n = 78
o = 79
p = 80
q = 81
r = 82
s = 83
t = 84
u = 85
v = 86
w = 87
x = 88
y = 89
z = 90
leftBracket = 91 /* [ */
backslash = 92 /* \ */
rightBracket = 93 /* ] */
graveAccent = 96 /* ` */
world1 = 161 /* non-US #1 */
world2 = 162 /* non-US #2 */
escape = 256
enter = 257
tab = 258
backspace = 259
insert = 260
delete = 261
right = 262
left = 263
down = 264
up = 265
pageUp = 266
pageDown = 267
home = 268
end = 269
capsLock = 280
scrollLock = 281
numLock = 282
printScreen = 283
pause = 284
fn0 = 289
fn1 = 290
fn2 = 291
fn3 = 292
fn4 = 293
fn5 = 294
fn6 = 295
fn7 = 296
fn8 = 297
fn9 = 298
fn10 = 299
fn11 = 300
fn12 = 301
fn13 = 302
fn14 = 303
fn15 = 304
fn16 = 305
fn17 = 306
fn18 = 307
fn19 = 308
fn20 = 309
fn21 = 310
fn22 = 311
fn23 = 312
fn24 = 313
fn25 = 314
kp0 = 320
kp1 = 321
kp2 = 322
kp3 = 323
kp4 = 324
kp5 = 325
kp6 = 326
kp7 = 327
kp8 = 328
kp9 = 329
kpDecimal = 330
kpDivide = 331
kpMultiply = 332
kpSubtract = 333
kpAdd = 334
kpEnter = 335
kpEqual = 336
leftShift = 340
leftControl = 341
leftAlt = 342
leftSuper = 343
rightShift = 344
rightControl = 345
rightAlt = 346
rightSuper = 347
menu = 348
}
Gamepad button number
type GamepadButton* = enum {
a
b
x
y
lt
rt
lb
rb
select
start
up
down
left
right
lstick
rstick
_count
}
Gamepad stick number
type GamepadStick* = enum {
left
right
}
fn getMousePos
fn getMousePos*(): th::Vf2 {
Returns the position of mouse cursor in relation to the screen.
fn getGlobalMousePos
fn getGlobalMousePos*(): th::Vf2 {
Returns the position of mouse cursor in relation to cam.
fn isPressed
fn isPressed*(code: Key): bool {
Returns true if key is pressed. Either use codes defined in the file above, or pass lower case char/number.
fn isPressedc
fn isPressedc*(code: char): bool {
Like isPressed
, but you can pass char as the code.
fn isJustPressed
fn isJustPressed*(code: Key): bool {
Returns, whether code was just pressed this loop.
fn isJustPressedc
fn isJustPressedc*(code: char): bool {
Like isJustPressed
, but you can pass char as the code.
fn isPressedRepeat
fn isPressedRepeat*(code: Key): bool {
Returns, whether code was just pressed this loop, with key repeat.
fn isPressedRepeatc
fn isPressedRepeatc*(code: char): bool {
Like isPressedRepeat
, but you can pass char as the code.
fn isJustReleased
fn isJustReleased*(code: Key): bool {
Returns true if a key was just released.
fn isJustReleasedc
fn isJustReleasedc*(code: char): bool {
Like isJustReleased
, but you can pass char as the code.
fn clear
fn clear*(code: Key) {
Clears both the pressed and justPressed state of a code.
fn clearc
fn clearc*(code: char) {
Like clear
, but you can pass char as the code.
fn getStr
fn getStr*(): str {
Returns a string entered by the user in the last cycle.
fn getMouseDelta
fn getMouseDelta*(): th::Vf2 {
Returns the difference between mouse positions in the last cycle. Will work
even if window.freezeCursor
is enabled.
fn getMouseScroll
fn getMouseScroll*(): th::Vf2 {
Returns the scroll wheel value
fn gamepads
fn gamepads*(): []Gamepad {
Returns a list of gamepads that are currently connected.
fn gamepad
fn gamepad*(): Gamepad {
Returns the connected gamepad.
fn isPressed
fn (g: ^Gamepad) isPressed*(button: GamepadButton): bool {
Returns true if the gamepad button is pressed.
fn isJustPressed
fn (g: ^Gamepad) isJustPressed*(button: GamepadButton): bool {
Returns true if the gamepad button was just pressed.
fn isJustReleased
fn (g: ^Gamepad) isJustReleased*(button: GamepadButton): bool {
Returns true if the gamepad button was just released.
fn pressure
fn (g: ^Gamepad) pressure*(button: GamepadButton): th::fu {
Returns the pressure of the gamepad button in the range [0, 1].
fn stick
fn (g: ^Gamepad) stick*(stick: GamepadStick): th::Vf2 {
Returns the movement of the gamepad stick in the range [-1, 1] for each axis.
fn rumble
fn (g: ^Gamepad) rumble*(left, right: th::fu) {
Rumbles/vibrates the gamepad.
Misc functions.
fn readall DEPRECATED
fn readall*(path: str): str {
Use std.freadall instaed.
fn stepify
fn stepify*(val, step: th::fu): th::fu {
Snaps a value to step.
fn maxf
fn maxf*(a, b: th::fu): th::fu {
fn minf
fn minf*(a, b: th::fu): th::fu {
struct Mesh
type Mesh* = struct {
// The mesh data.
d: []bool
// The dimensions and position of the mesh, r.w == w * s
r: rect::Rect
// Width of the mesh. Used to adress the mesh data.
w: th::uu
// Scale of one cell (cell is a square)
s: th::fu
}
Mesh is a 2d array of bool cells. If a cell is true, the cell can be used
in a path. The mesh is located in a world using the r
field. A cell can
have an arbitrary size as specified by s
.
The mesh can be edited using the addQuad
method, but it should be trivial
to add your own, similar methods.
Please use the mk
constructor to construct a Mesh
, unless you really
know what you're doing.
fn mk
fn mk*(r: rect::Rect, s: th::fu): Mesh {
Creates a new nav mesh.
r
: the rectangle of the mask
's'
: the scale of the mask
fn Mesh.addQuad
fn (m: ^Mesh) addQuad*(q: th::Quad) {
Sets mask's fields overlapping q
to false
.
fn Mesh.nav
fn (m: ^Mesh) nav*(p1, p2: th::Vf2): []th::Vf2 {
Navigates between p1
and p2
. Returns the path as an array of th.Vf2
s.
If it doesn't find any path, or one of the points is outside of the mask,
returns an empty array.
fn Mesh.draw
fn (m: ^Mesh) draw*(alpha: real32 = 1.0) {
Draws the mesh for debugging purposes.
Particles allow for performant and random particle systems.
struct Particle
type Particle* = struct {
Particle struct. You can tweak the start_time for godot-like explossivness.
struct Emitter
type Emitter* = struct {
pos: th::Vf2 // position
dm: th::Vf2 // size of the emittion area
gravity: th::Vf2 // gravity
repeat: bool // if false, particle won't be renewed
active: bool // false, if there aren't any active particles anymore
angle: th::Vf2 // angle in which particles are emitted
lifetime: th::uu // lifetime of particles
lifetimeRandomness: th::fu // randomness in %/100
velocity: th::fu // velocity
velocityRandomness: th::fu // randomness in %/100
size: th::fu // size
sizeRandomness: th::fu // randomness in %/100
maxSize: th::fu // size at the end of particles lifetime
rotation: th::fu
rotationRandomness: th::fu
maxRotation: th::fu
colors: []uint32 // array of colors, which are interpolated between
particles: []Particle // list of particles
}
Emitter. This is where everything is configured.
fn Emitter.draw
fn (e: ^Emitter) draw*(t: int32) {
Draws and updates the particles.
fn Emitter.genParticles
fn (e: ^Emitter) genParticles*(time, count: uint, explosiveness: th::fu = 0.0) {
Generates particles for an emitter. The time specifies the time the first
particles is emitted. The explosiveness argument specifies the interval at
which particles are emitted using this formula:
/ umka / e.lifetime / count * explosiveness /
struct Rect
type Rect* = struct {
x, y, w, h: th::fu
}
A set of points representing a rectangle.
fn mk
fn mk*(x, y, w, h: th::fu): Rect {
fn mk
fn fromVf2*(p: th::Vf2, dm: th::Vf2): Rect {
Creates a rect from two Vf2s - the position and the dimensions.
fn Rect.getPos
fn (r: ^Rect) getPos*(): th::Vf2 {
fn Rect.getDims
fn (r: ^Rect) getDims*(): th::Vf2 {
fn Rect.getEnd
fn (r: ^Rect) getEnd*(): th::Vf2 {
returns where the second point of the rectangle lies.
fn Rect.transformed
fn (r: ^Rect) transformed*(t: th::Transform): th::Quad {
Transforms a rect into a quad. Order:
- scale
- rotation
- position
fn Rect.shrink
fn (r: ^Rect) shrink*(p: th::Vf2): Rect {
Shrink the rectangle by p
pixels from all sides.
fn Rect.shift
fn (r: ^Rect) shift*(p: th::Vf2): Rect {
Shift the rectangle by p
pixels.
fn Rect.scale
fn (r: ^Rect) scale*(p: th::Vf2): Rect {
Multiply the dimensions by p
fn Rect.center
fn (r: ^Rect) center*(): th::Vf2 {
Returns the position, which is the center of the rect.
fn Rect.centerWithinRect
fn (r: ^Rect) centerWithinRect*(child: Rect): Rect {
Centers child
with the rect r
.
opaque RenderTarget
type RenderTarget* = struct { _: ^struct{} }
An image you can render to.
fn mk
fn mk*(size: th::Vf2, filter: image::Filter): (RenderTarget, std::Err) {
Creates a render target you can draw to, like to a window.
Filter specifies specfifies filtering for resulting image.
Image can be retrieved via toImage
.
fn RenderTarget.begin
fn (rt: ^RenderTarget) begin*(): std::Err {
Begins the render target rendering pass.
fn RenderTarget.end
fn (rt: ^RenderTarget) end*(): std::Err {
Ends the render target rendering pass.
fn RenderTarget.toImage
fn (rt: ^RenderTarget) toImage*(): image::Image {
Returns the image of the render target. The resulting image has the same
lifetime as the base RenderTarget. If you need to use it past the lifetime
of the RenderTarget, use the copy method.
Do not call setfilter
on the resulting image.
A module for importless communication between modules. A signal is a set of callbacks. You can use signals directly in your own structs if you want them to be instance specific, of you can use global signals which are adressed by a string name.
type Callback
type Callback* = fn(args: []any)
args
is a list of arguments passed to the emit
method.
type Id
type Id* = uint
type Signal
type Signal* = map[Id]Callback
fn mk
fn mk*(): Signal {
Signal
constructor
fn Signal.register
fn (this: ^Signal) register*(callback: Callback): Id {
Registers a callback to a signal and returns the callback id.
fn Signal.remove
fn (this: ^Signal) remove*(id: Id) {
Removes a callback by id.
fn Signal.emit
fn (this: ^Signal) emit*(args: ..any) {
Emits a signal.
fn Signal.clear
fn (this: ^Signal) clear*() {
Removes all signal handlers.
fn register
fn register*(name: str, callback: Callback): Id {
Registers a callback to a global signal. There is no need to explicitly create global signals. Returns id of the added callback
fn remove
fn remove*(name: str, id: Id) {
Removes a callback from a global signal by id.
fn remove
fn clear*(name: str) {
Removes all signal handlers from a global signal.
fn emit
fn emit*(name: str, args: ..any): std::Err {
Calls all callbacks associated with the passed global signal name.
Module with useful variables and types. Variables: time, delta, platform Constants: black, white, red, green, blue, yellow, magenta, cyan.
Tophat type aliases
type fu* = real32
// standard type for integer values
type iu* = int32
// standard type for unsigned values
type uu* = uint32
standard type for real values
enum ErrCode
type ErrCode* = enum (int32) {
ok = 0
failure = 1
io
bad_enum
bad_action
bad_input
alloc
already
out_of_bounds
}
struct Vf2
type Vf2* = struct {
x, y: fu
}
vector 2
fn mkVf2
fn mkVf2*(x: fu = 0, y: fu = 0): Vf2 {
return Vf2{x, y}
}
Vf2 constructor
fn Vf2.rotated
fn (p: ^Vf2) rotated*(origin: Vf2, rot: fu): Vf2 {
rotates p
around origin
with rot
in degrees
fn Vf2.distanceTo
fn (p1: ^Vf2) distanceTo*(p2: Vf2): fu {
distance between p1 and p2
fn Vf2.angleTo
fn (p1: ^Vf2) angleTo*(p2: Vf2): real {
Angle between p1 and p2
fn Vf2.abs
fn (p: ^Vf2) abs*(): Vf2 {
Absolute value of a vector.
fn Vf2.round
fn (p: ^Vf2) round*(): Vf2 {
Rounds a vector.
fn Vf2.trunc
fn (p: ^Vf2) trunc*(): Vf2 {
Truncates a vector.
fn Vf2.floor
fn (p: ^Vf2) floor*(): Vf2 {
Floors a vector.
fn Vf2.ceil
fn (p: ^Vf2) ceil*(): Vf2 {
Ceils a vector.
fn vf2f
fn vf2f*(f: fu): Vf2 {
Creates a vector with both x and y set to f
fn Vf2.sub
fn (p: ^Vf2) sub*(p2: Vf2): Vf2 {
Subtracts a vector from another one.
fn Vf2.subf
fn (p: ^Vf2) subf*(f: fu): Vf2 {
Subtracts a fu from a vector.
fn Vf2.add
fn (p: ^Vf2) add*(p2: Vf2): Vf2 {
Adds a vector to another one.
fn Vf2.addf
fn (p: ^Vf2) addf*(f: fu): Vf2 {
Adds a fu to a vector.
fn Vf2.div
fn (p: ^Vf2) div*(p2: Vf2): Vf2 {
Divides a vector by another one.
fn Vf2.divf
fn (p: ^Vf2) divf*(f: fu): Vf2 {
Divides a vector by a fu.
fn Vf2.mul
fn (p: ^Vf2) mul*(p2: Vf2): Vf2 {
Multiplies a vector by another one.
fn Vf2.mulf
fn (p: ^Vf2) mulf*(f: fu): Vf2 {
Multiplies a vector by a fu.
fn Vf2.mag
fn (p: ^Vf2) mag*(): fu {
Returns the magnitude of a vector p.
fn Vf2.norm
fn (p: ^Vf2) norm*(): Vf2 {
Normalizes a vector.
fn Vf2.dot
fn (p: ^Vf2) dot*(q: Vf2): fu {
Calculates dot product between 2 vectors.
struct Transform
type Transform* = struct {
p: Vf2 // position
s: Vf2 // scale
o: Vf2 // origin
r: fu // rotation
}
Struct defining transformation. Used for example by entities.
fn mkTransform
fn mkTransform*(p: Vf2, s: Vf2 = Vf2{1, 1}, o: Vf2 = Vf2{0, 0}, r: fu = 0.0): Transform {
Transform constructor
fn Transform.transformed
fn (o: ^Transform) transformed*(t: Transform): Transform {
Transforms a transform with another transform.
fn Vf2.transformed
fn (v: ^Vf2) transformed*(t: Transform): Vf2 {
Transforms a vf2 to another vf2. Order:
- scale
- rotation
- position
This allows conversion from a relative to an absolute vf2.
type Quad
type Quad* = [4]Vf2
fn Quad.transformed
fn (q: ^Quad) transformed*(t: Transform): Quad {
Transforms a quad with a transform.
fn Quad.getMax
fn (q: ^Quad) getMax*(): Vf2 {
Gets the maximum coordinate.
fn Quad.getMin
fn (q: ^Quad) getMin*(): Vf2 {
Gets the minimum coordinate.
fn Quad.getDims
fn (q: ^Quad) getDims*(): Vf2 {
Returns the dimensions of the quad's bounding box
fn getGlobal
fn getGlobal*(): ^struct{} {
returns a pointer to the th_global. Set this as your extensions thg.
fn getFuncs
fn getFuncs*(): ^struct{} {
returns pointer to tophat functions. Pass this to th_ext_set.
var enableErrrors
var enableErrors*: bool = true
If true, errors will result in a call to exit(255), otherwise printf is used.
Color constants
const (
black* = 0xff
white* = 0xffffffff
red* = 0xff0000ff
green* = 0x00ff00ff
blue* = 0x0000ffff
yellow* = 0xffff00ff
magenta* = 0xff00ffff
cyan* = 0x00ffffff
)
enum Platform
type Platform* = enum {
unknown
linux
windows
macOs
web
}
Misc variables
var (
// time in ms from start of the game
time*: uint
// length of the last frame in ms
delta*: int
// platform tophat is running on
platform*: Platform
)
fn convPath
fn convPath*(path: str): str {
Expands the path to a platform specific path.
Tilemaps allow for easy level construction and fast collisions. You can even use them for some games instead of entities (tetris comes to mind)
Direction constants used for autotile
const (
top* = 1
right* = 2
bot* = 4
left* = 8
)
struct Tilemap
type Tilemap* = struct {
atlas: atlas::Atlas
pos: th::Vf2
w: th::uu // width of tilemap
cells: []th::uu // all cells (this will draw the tile in tiles with number in cells - 1)
collMask: []bool // if true, the tile collides
scale: th::fu
}
Tilemap struct
fn mk
fn mk*(w, h: th::uu, at: atlas::Atlas, scale: th::fu = 1): Tilemap {
Make a tilemap with all cells set to 0.
fn mk2
fn mk2*(cells: []th::uu, w: th::uu, at: atlas::Atlas, scale: th::fu = 1): Tilemap {
Make a tilemap from a list of cells.
fn Tilemap.has
fn (t: ^Tilemap) has*(x, y: int): bool {
Check if a tile exists at the given coordinates.
fn Tilemap.edit
fn (t: ^Tilemap) edit*(x, y, tile: int){
Edit a tile in the tilemap.
fn Tilemap.get
fn (t: ^Tilemap) get*(x, y: int): int {
Get a tile from the tilemap.
fn Tilemap.draw
fn (t: ^Tilemap) draw*(tr: th::Transform, tint: uint32 = 0xFFFFFFFF) {
Draws the tilemap.
fn Tilemap.getColl
fn (t: ^Tilemap) getColl*(e: ent::Ent, ic: ^th::Vf2, pos: ^th::Vf2): bool {
Checks whether e
collides with any of the tiles in t
, which are in the
collmask.
ic
[out] - the position where a collision occuredpos
[out] - coordinates of a tile where a collision occured
Note: While there may be multiple collisions with a tilemap, this function will only return one.
fn Tilemap.getCollLine
fn (t: ^Tilemap) getCollLine*(b, e: th::Vf2, ic: ^th::Vf2): bool {
Check for a collision between a tilemap and a line. ic
will be a point of
an intersection, if there is one.
fn Tilemap.autotile
fn (t: ^Tilemap) autotile*(src, tileCfg: []th::uu, tile: th::uu) {
Autotile turns all tile
tiles in src
into tiles in tileCfg
, so they
follow up correctly. tileCfg
is an array of 16 tiles. They are placed in
a way where OR of all the places where the tile continues (top, right bot,
right). The constants for them are defined in this file. Example:
tileCfg[top | bot] = 21
top | bot would look something like this: |
ui.um
offers an immediate GUI library suitable both for simple game menus
and more complex applications.
struct BoxStyle
type BoxStyle* = struct {
img: image::Image
outer, inner: rect::Rect
scale: th::fu
color: uint32
}
BoxStyle
describes how a box within the GUI is styled. In this case box
can be anything, ranging from a button to a container. By default the box
is drawn using the image.Image.drawNinepatch
method. However if the image
is invalid, a rectangle with color color
is drawn.
interface Font
type Font* = interface {
draw(text: str, pos: th::Vf2, color: uint32, scale: th::fu = 1.0)
measure(test: str): th::Vf2
}
This interface is used by all elements that draw text. A font.Font
implements this interface.
struct PixelFont
type PixelFont* = struct { }
This struct implement the Font
interface using the canvas.um
pixel font.
struct Style
type Style* = struct {
// current font
ft: Font
// font scale
ftScale: th::fu
// text color
ftColor: uint32
// Positive box - i. e. unpressed button
posBox: BoxStyle
// Negative box - i. e. pressed button, text box
negBox: BoxStyle
// Used to draw containers
containerBox: BoxStyle
}
Style
is used as a global state for styling the GUI.
interface Container
type Container* = interface {
// This adds a rectangle to the container, and returns the rectangle
// which was actually added (the container can modify the rectangle).
// See individual containers for further documentation.
pushRect(r: rect::Rect): rect::Rect
getDims(): rect::Rect
}
Containers are used to layout elements or other containers.
struct Gui
type Gui* = struct {
// user context passed to layout functions
ctx: any
// the index of the current selection. TODO implement properly
selection: int
// true, if the layout is being evaluated, not drawn
isEval: bool
// contains more unexported fields
This is the main struct of any UI. Styles and containers are in a stack.
fn mk
fn mk*(r: rect::Rect, s: Style): Gui
Creates a new gui spanning r
, with style s
.
type LayoutFn
type LayoutFn* = fn(gui: ^Gui)
The layout function calls different element or container methods to create
the user interface itself. It is called in the eval
and draw
.
fn BoxStyle.draw
fn (this: ^BoxStyle) draw*(r: rect::Rect) {
Draws a rectangle using a BoxStyle
fn Gui.pushStyle
fn (this: ^Gui) pushStyle*(s: Style) {
Pushes a style onto the style stack.
fn Gui.popStyle
fn (this: ^Gui) popStyle*() {
Pops a style from the style stack.
fn Gui.getStyle
fn (this: ^Gui) getStyle*(): ^Style {
Returns a pointer to the style atop the style stack.
fn Gui.getContainer
fn (this: ^Gui) getContainer*(): Container {
Returns the container atop the container stack.
fn Gui.pushRect
fn (this: ^Gui) pushRect*(r: rect::Rect): rect::Rect {
Shortcut to this.getContainer().pushRect(r)
fn Gui.getDims
fn (this: ^Gui) getDims*(): rect::Rect {
Shortcut to this.getContainer().getDims(r)
fn Gui.dupStyle
fn (this: ^Gui) dupStyle*() {
Duplicates the current style.
fn Gui.pushContainer
fn (this: ^Gui) pushContainer*(c: Container) {
Pushes a container onto the container stack.
fn Gui.popContainer
fn (this: ^Gui) popContainer*() {
Pops a container from the container stack.
fn Gui.eval
fn (this: ^Gui) eval*(layout: LayoutFn) {
Runs the evaluation phase on layout
.
fn Gui.draw
fn (this: ^Gui) draw*(layout: LayoutFn) {
Runs the draw phase on layout
.
enum BoxGrow
type BoxGrow* = enum {
dimension
subdiv
span
pxSpan
}
The different types of "growing" the box can do.
enum BoxDirection
type BoxDir* = enum {
down
right
up
left
}
Direction in which the box will grow.
struct BoxConfig
type BoxConfig* = struct {
// dimension to grow by if `BoxGrowDimension` is used
dimension: th::fu
// number of subdivisions if `BoxGrowSubdivisions` is used
subdivisions: uint
// the grow type
growType: BoxGrow
// the grow direction
dir: BoxDir
// Specifies the values used with BoxGrowSpan nad BoxGrowPxSpan.
// If BoxGrowSpan is used, 1 equals the size of the box divided by the sum
// of all spans.
// If BoxGrowPxSpan is used, 1 equals one pixel.
span: []th::fu
// rect passed to the current container
rect: rect::Rect
// padding inserted after each element
padding: th::fu
}
Configuration of the Box
container.
struct Box
type Box* = struct {
grow: th::fu
spanCursor: int
dm: rect::Rect
cfg: BoxConfig
}
Box
is the main layout. It puts the elements next to each other,
according to the config.
If the dimensions of the rect passed to pushRect
are non zero, they will
be kept. Position is always forced.
fn Gui.box
fn (gui: ^Gui) box*(cfg: BoxConfig = {
dimension: 30,
growType: BoxGrow.dimension,
dir: BoxDir.down }) {
Adds the Box
container to the gui.
struct StackConfig
type StackConfig* = struct {
rect: rect::Rect
padding: th::fu
}
Configuration for the Stack
container.
struct Stack
type Stack* = struct {
dm: rect::Rect
cfg: StackConfig
}
The stack container puts elements on top of each other.
If a property of the rect passed to pushRect
is zero, it will be changed
to an equivalent property of the containers' dimensions (minus the padding),
else it will stay the same. This means stack can be used either to put
multiple elements/containers on top of each other, or for absolutely
positioned elements.
fn Gui.stack
fn (gui: ^Gui) stack*(cfg: StackConfig = {}) {
Adds the Stack
container to the gui.
struct ScrollAreaConfig
type ScrollAreaConfig* = struct {
rect: rect::Rect
// scroll speed. Default is 1
speed: real32
// if true, scrolling will be horizontal
horizontal: bool
}
Configuration for the scroll area.
struct ScrollArea
type ScrollArea* = struct {
dm: rect::Rect
cfg: ScrollAreaConfig
scroll: ^real32
maxScroll: real32
}
Scroll area is a container which allows the user to scroll. It acts as a stack container, but all the elements are shifted by the scroll.
fn Gui.scrollArea
fn (gui: ^Gui) scrollArea*(scroll: ^real32, maxScroll: real32, cfg: ScrollAreaConfig = {}) {
Pushes a scroll area. scroll
is both input and output value. Both scroll
and maxScroll
are in pixels.
struct ButtonConfig
type ButtonConfig* = struct {
rect: rect::Rect
}
Configuration for the button.
fn Gui.button
fn (gui: ^Gui) button*(cfg: ButtonConfig = {}): bool {
Adds a button to the gui. The button acts like a Stack
container, but it
is drawn using the pos/nexBox styles and handles clicks. If the button is
pressed and the gui is in the eval phase, the return value will be true.
struct LabelConfig
type LabelConfig* = struct {
// centers the label along the X axis, enables `stretchX`
centerX: bool
// centers the label along the Y axis, enables `stretchY`
centerY: bool
// if false, the rect passed to `pushRect` will have the width of
// the text, else it will be 0
stretchX: bool
// if false, the rect passed to `pushRect` will have the height of
// the text, else it will be 0
stretchY: bool
// forces the rectangle the label will use
rect: rect::Rect
}
fn Gui.label
fn (gui: ^Gui) label*(text: str, cfg: LabelConfig = {
Draws a label using the current font style.
fn Gui.qbutton
fn (gui: ^Gui) qbutton*(text: str, cfg: ButtonConfig = {}): bool {
Adds a button with a label to gui.
struct TextBoxConfig
type TextBoxConfig* = struct {
// force the rect of the text box
rect: rect::Rect
}
struct TextBox
type TextBox* = struct {
// index of the cursor
cursor: int
// contains other unexported rules...
fn TextBox.clear
fn (this: ^TextBox) clear*() {
Clears the textbox
fn TextBox.getBuf()
fn (this: ^TextBox) getBuf*(): str {
Get the content of the textbox.
fn TextBox.setBuf()
fn (this: ^TextBox) setBuf*(s: str) {
Get the content of the textbox.
fn Gui.textBox
fn (gui: ^Gui) textBox*(tb: ^TextBox, cfg: TextBoxConfig = {}) {
Adds a single line textbox to the gui. TODO:
- right-to-left unicode is not supported.
- no selection
- multiline
- copy paste (now implemented but in a limited way due to the lack of selection)
- common input shortcuts
- ctrl+delete / ctrl+backspace (delete word)
struct ImageConfig
type ImageConfig* = struct {
stretchX, stretchY: bool
centerX, centerY: bool
color: uint32
scale: th::Vf2
rect: rect::Rect
}
Configuration for the images element. Behaves similarly to labels.
fn Gui.image
fn (gui: ^Gui) image*(i: image::Image, cfg: ImageConfig = {
stretchX: true,
stretchY: true,
color: th::white,
scale: { 1, 1 } }) {
Draws an image.
fn getDefaultStyle
fn getDefaultStyle*(): Style {
Returns the default tophat ui style.
fn mk
fn mk*(r: rect::Rect, s: Style): Gui {
Creates a GUI instance.
Cursor types
type Cursor* = enum {
system = 0 // Default system cursor
arrow // Normal cursor; Arrow cursor
iBeam // 'I' text cursor; I-Beam
crosshair // '+' cursor; Select region cursor
finger // Index finger pointing cursor; Click cursor
sizeEW // '<->' cursor; Resize width cursor; Resize horizontally cursor; East-West resize cursor
sizeNS // Resize height cursor; Resize vertically cursor; North-South resize cursor
sizeNWSE // Resize width and height from the right side cursor; Northwest-Southeast resize cursor
sizeSWNE // Resize width and height from the left side cursor; Southwest-Northeast resize cursor
sizeAll // Resize all cursor; Move cursor
no // '(/)' cursor; Disabled cursor; Disallowed cursor
count_
}
Window dimensions
var (
w*, h*: int32
)
Viewport size
var wp*: th::Vf2
signal OnFrame
var onFrame*: signal::Signal
signal OnDestroy
var onDestroy*: signal::Signal
fn setViewport
fn setViewport*(dm: th::Vf2) {
Sets the dimensions of the viewport. The dimensions are saved in the wp
variable.
dm
: dimension of the viewport
fn isDpiEnabled
fn isDpiEnabled*(): bool {
Returns true if DPI awareness was enabled
fn getDpiScaleFactor
fn getDpiScaleFactor*(): th::fu {
Returns the DPI scaling of the current window.
If dpiAware
was not enabled in window setup, this function will return 1.0 (default scaling).
fn setup
fn setup*(title: str = "tophat game", width: int = 400, height: int32 = 400) {
Sets up the engine and opens a window.
fn setFullscreen
fn setFullscreen*(fullscreen: bool) {
Makes window go full screen
fn isFullscreen
fn isFullscreen*(): bool {
Returns true if window is fullscreen
fn getDims
fn getDims*(): th::Vf2 {
Returns dimensions of the window in screen pixels.
fn setTargetFps
fn setTargetFps*(fps: int) {
Sets the fps limit.
fps
: amount of fps the limit should be set to
fn setTitle
fn setTitle*(title: str) {
Sets the title of the window.
fn setDims
fn setDims*(dm: th::Vf2) {
Sets the dimensions of the window.
dm
: the target dimensions in screen pixels
fn setIcon
fn setIcon*(img: image::Image) {
Sets the window icon.
fn showCursor
fn showCursor*(show: bool) {
Show or hide the cursor, linux only.
fn freezeCursor
fn freezeCursor*(freeze: bool) {
Freezes the cursor in place. input.getMouseDelta
will still report mouse
movements. The cursor will be automatically hidden.
fn setCursor
fn setCursor*(cursor: Cursor) {
Allows you to set the displaying cursor. Refer to the cursors section for available cursors.
fn requestExit
fn requestExit*() {
Requests the window to close.
fn setClipboard
fn setClipboard*(s: str) {
Puts a string to the system clipboard.
fn getClipboard
fn getClipboard*(): str {
Gets a string from the system clipboard.
fn setViewportOffset
fn setViewportOffset*(s: th::Vf2) {
Sets the offset of the viewport.
fn getViewportOffset
fn getViewportOffset*(): th::Vf2 {
Gets the offset of the viewport (as set by setViewportOffset
)