lofivor/docs/reference.md

272 lines
6.7 KiB
Markdown

# quick reference
## fixed-point math
32.32 format: 32 integer bits, 32 fractional bits.
```zig
const Fixed = struct { raw: i64 };
// constants
const ONE = Fixed{ .raw = 1 << 32 };
const HALF = Fixed{ .raw = 1 << 31 };
const PI = Fixed{ .raw = 13493037705 }; // pi * 2^32
// from int
Fixed{ .raw = @as(i64, n) << 32 }
// to float (rendering only!)
@as(f32, @floatFromInt(f.raw)) / 4294967296.0
// add/sub: direct
a.raw + b.raw
a.raw - b.raw
// mul: widen to i128
@intCast((@as(i128, a.raw) * b.raw) >> 32)
// div: shift first
@intCast(@divTrunc(@as(i128, a.raw) << 32, b.raw))
```
## trig lookup tables
generate at comptime:
```zig
const TABLE_SIZE = 1024;
const sin_table: [TABLE_SIZE]Fixed = blk: {
var table: [TABLE_SIZE]Fixed = undefined;
for (0..TABLE_SIZE) |i| {
const angle = @as(f64, @floatFromInt(i)) * std.math.pi * 2.0 / TABLE_SIZE;
const s = @sin(angle);
table[i] = .{ .raw = @intFromFloat(s * 4294967296.0) };
}
break :blk table;
};
pub fn sin(angle: Fixed) Fixed {
// angle in radians, normalize to table index
const two_pi = Fixed{ .raw = 26986075409 }; // 2*pi * 2^32
var a = @mod(angle.raw, two_pi.raw);
if (a < 0) a += two_pi.raw;
const idx = @as(usize, @intCast((a * TABLE_SIZE) >> 32)) % TABLE_SIZE;
return sin_table[idx];
}
pub fn cos(angle: Fixed) Fixed {
const half_pi = Fixed{ .raw = 6746518852 }; // pi/2 * 2^32
return sin(Fixed{ .raw = angle.raw + half_pi.raw });
}
```
## networking
### packet format
```
byte 0: packet type
bytes 1-4: frame number (u32 little-endian)
byte 5: player id
bytes 6-9: checksum (u32 little-endian)
bytes 10+: payload
```
### packet types
| type | id | payload |
|------|------|---------|
| INPUT | 0x01 | move(i8), angle_delta(i8), power_delta(i8), fire(u8) |
| SYNC | 0x02 | full GameState blob |
| PING | 0x03 | timestamp(u64) |
| PONG | 0x04 | original timestamp(u64) |
### zig udp basics
```zig
const std = @import("std");
const net = std.net;
// create socket
const sock = try std.posix.socket(std.posix.AF.INET, std.posix.SOCK.DGRAM, 0);
defer std.posix.close(sock);
// bind (server)
const addr = net.Address.initIp4(.{ 0, 0, 0, 0 }, 7777);
try std.posix.bind(sock, &addr.any, addr.getLen());
// send
const dest = net.Address.initIp4(.{ 127, 0, 0, 1 }, 7777);
_ = try std.posix.sendto(sock, &packet_bytes, 0, &dest.any, dest.getLen());
// receive
var buf: [1024]u8 = undefined;
var src_addr: std.posix.sockaddr = undefined;
var src_len: std.posix.socklen_t = @sizeOf(std.posix.sockaddr);
const len = try std.posix.recvfrom(sock, &buf, 0, &src_addr, &src_len);
```
## raylib-zig setup
### build.zig.zon
```zig
.{
.name = "lofivor",
.version = "0.0.1",
.dependencies = .{
.raylib_zig = .{
.url = "git+https://github.com/Not-Nik/raylib-zig#devel",
.hash = "...", // zig fetch will tell you
},
},
}
```
### build.zig
```zig
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const raylib_dep = b.dependency("raylib_zig", .{
.target = target,
.optimize = optimize,
});
const exe = b.addExecutable(.{
.name = "lofivor",
.root_module = b.createModule(.{
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
}),
});
exe.root_module.addImport("raylib", raylib_dep.module("raylib"));
exe.linkLibrary(raylib_dep.artifact("raylib"));
b.installArtifact(exe);
const run_cmd = b.addRunArtifact(exe);
run_cmd.step.dependOn(b.getInstallStep());
const run_step = b.step("run", "run the game");
run_step.dependOn(&run_cmd.step);
}
```
### basic window
```zig
const rl = @import("raylib");
pub fn main() !void {
rl.initWindow(800, 600, "lofivor");
defer rl.closeWindow();
rl.setTargetFPS(60);
while (!rl.windowShouldClose()) {
rl.beginDrawing();
defer rl.endDrawing();
rl.clearBackground(rl.Color.black);
rl.drawText("hello", 10, 10, 20, rl.Color.white);
}
}
```
## rendering glow effect
### render texture + shader
```zig
const rl = @import("raylib");
// load shader
const blur_shader = rl.loadShader(null, "shaders/blur.fs");
defer rl.unloadShader(blur_shader);
// create render textures
const game_tex = rl.loadRenderTexture(800, 600);
const blur_tex = rl.loadRenderTexture(800, 600);
defer rl.unloadRenderTexture(game_tex);
defer rl.unloadRenderTexture(blur_tex);
// in game loop:
// 1. draw game to texture
rl.beginTextureMode(game_tex);
rl.clearBackground(.{ .r = 10, .g = 10, .b = 18, .a = 255 });
drawGame(&state);
rl.endTextureMode();
// 2. blur pass (horizontal)
rl.beginTextureMode(blur_tex);
rl.beginShaderMode(blur_shader);
// set direction uniform to (1, 0)
rl.drawTextureRec(game_tex.texture, .{ .x = 0, .y = 0, .width = 800, .height = -600 }, .{ .x = 0, .y = 0 }, .white);
rl.endShaderMode();
rl.endTextureMode();
// 3. blur pass (vertical) + composite
rl.beginDrawing();
// draw original
rl.drawTextureRec(game_tex.texture, .{ .x = 0, .y = 0, .width = 800, .height = -600 }, .{ .x = 0, .y = 0 }, .white);
// additive blend blurred
rl.beginBlendMode(.additive);
rl.beginShaderMode(blur_shader);
// set direction uniform to (0, 1)
rl.drawTextureRec(blur_tex.texture, .{ .x = 0, .y = 0, .width = 800, .height = -600 }, .{ .x = 0, .y = 0 }, .white);
rl.endShaderMode();
rl.endBlendMode();
rl.endDrawing();
```
### drawing lines
```zig
// basic line
rl.drawLine(x1, y1, x2, y2, color);
// thick line
rl.drawLineEx(.{ .x = x1, .y = y1 }, .{ .x = x2, .y = y2 }, thickness, color);
// terrain as connected lines
for (0..SCREEN_WIDTH - 1) |x| {
const y1 = SCREEN_HEIGHT - terrain.heights[x].toInt();
const y2 = SCREEN_HEIGHT - terrain.heights[x + 1].toInt();
rl.drawLine(@intCast(x), y1, @intCast(x + 1), y2, .{ .r = 0, .g = 255, .b = 0, .a = 255 });
}
```
## checksum
```zig
pub fn checksum(state: *const GameState) u32 {
var h = std.hash.Fnv1a_32.init();
h.update(std.mem.asBytes(&state.tick));
h.update(std.mem.asBytes(&state.players));
h.update(std.mem.asBytes(&state.wind.raw));
if (state.projectile) |p| h.update(std.mem.asBytes(&p));
return h.final();
}
```
## common fixed-point constants
```zig
pub const ZERO = Fixed{ .raw = 0 };
pub const ONE = Fixed{ .raw = 1 << 32 };
pub const HALF = Fixed{ .raw = 1 << 31 };
pub const TWO = Fixed{ .raw = 2 << 32 };
pub const PI = Fixed{ .raw = 13493037705 };
pub const TWO_PI = Fixed{ .raw = 26986075409 };
pub const HALF_PI = Fixed{ .raw = 6746518852 };
// game constants
pub const GRAVITY = Fixed{ .raw = 42949673 }; // ~0.01
pub const WIND_FACTOR = Fixed{ .raw = 4294967 }; // ~0.001
pub const MAX_POWER = Fixed{ .raw = 100 << 32 };
```