Added background image, TTF loading, rgb color struct

This commit is contained in:
Nathan Anderson 2025-01-05 20:03:15 -07:00
parent 8ece62d49a
commit 52097b3861
13 changed files with 386 additions and 13 deletions

BIN
assets/background.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

93
assets/fonts/LICENSE Normal file
View File

@ -0,0 +1,93 @@
Copyright 20222024 Helena Zhang (helenazhang.com).
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://openfontlicense.org
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

48
assets/fonts/README.md Normal file
View File

@ -0,0 +1,48 @@
# Nerd Fonts
This is an archived font from the Nerd Fonts release v3.3.0.
For more information see:
* https://github.com/ryanoasis/nerd-fonts/
* https://github.com/ryanoasis/nerd-fonts/releases/latest/
# Departure Mono
**Departure Mono** a monospaced pixel font with a lo-fi, techy vibe.
For more information have a look at the upstream website: https://github.com/rektdeckard/departure-mono
Version: v1.422
## Which font?
### TL;DR
* Pick your font family:
* If you are limited to monospaced fonts (because of your terminal, etc) then pick a font with `Nerd Font Mono` (or `NFM`).
* If you want to have bigger icons (usually around 1.5 normal letters wide) pick a font without `Mono` i.e. `Nerd Font` (or `NF`). Most terminals support this, but ymmv.
* If you work in a proportional context (GUI elements or edit a presentation etc) pick a font with `Nerd Font Propo` (or `NFP`).
### Ligatures
Ligatures are generally preserved in the patched fonts.
Nerd Fonts `v2.0.0` had no ligatures in the `Nerd Font Mono` fonts, this has been dropped with `v2.1.0`.
If you have a ligature-aware terminal and don't want ligatures you can (usually) disable them in the terminal settings.
### Explanation
Once you narrow down your font choice of family (`Droid Sans`, `Inconsolata`, etc) and style (`bold`, `italic`, etc) you have 2 main choices:
#### `Option 1: Download already patched font`
* For a stable version download a font package from the [release page](https://github.com/ryanoasis/nerd-fonts/releases)
* Direct links for [DepartureMono.zip](https://github.com/ryanoasis/nerd-fonts/releases/latest/download/DepartureMono.zip) or [DepartureMono.tar.xz](https://github.com/ryanoasis/nerd-fonts/releases/latest/download/DepartureMono.tar.xz)
#### `Option 2: Patch your own font`
* Patch your own variations with the various options provided by the font patcher (i.e. not include all symbols for smaller font size)
For more information see: [The FAQ](https://github.com/ryanoasis/nerd-fonts/wiki/FAQ-and-Troubleshooting#which-font)
[SIL-RFN]:http://scripts.sil.org/cms/scripts/page.php?item_id=OFL_web_fonts_and_RFNs#14cbfd4a

View File

@ -6,6 +6,8 @@ pub const GameState = struct {
r: u8 = 0xff, r: u8 = 0xff,
g: u8 = 0xff, g: u8 = 0xff,
b: u8 = 0xff, b: u8 = 0xff,
SCREEN_WIDTH: u32 = 640,
SCREEN_HEIGHT: u32 = 480,
slime_factory: sf.SlimeFactory = undefined, slime_factory: sf.SlimeFactory = undefined,
renderer: *sdl.struct_SDL_Renderer = undefined, renderer: *sdl.struct_SDL_Renderer = undefined,

View File

@ -6,6 +6,8 @@ const assert = std.debug.assert;
const SCREEN_WIDTH = 640; const SCREEN_WIDTH = 640;
const SCREEN_HEIGHT = 480; const SCREEN_HEIGHT = 480;
const log = sdl.SDL_Log; const log = sdl.SDL_Log;
const GameText = @import("./text.zig").GameText;
const RGBAColor = @import("./utils/rgb_color.zig").RGBAColor;
var window: *sdl.struct_SDL_Window = undefined; var window: *sdl.struct_SDL_Window = undefined;
// var screen_surface: *sdl.struct_SDL_Surface = undefined; // var screen_surface: *sdl.struct_SDL_Surface = undefined;
@ -24,8 +26,11 @@ pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){}; var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator(); const allocator = gpa.allocator();
const texture = try atils.loadTexture(renderer, "assets/png_test.png"); const bg_texture = try atils.loadTexture(renderer, "assets/background.png");
defer sdl.SDL_DestroyTexture(texture); defer sdl.SDL_DestroyTexture(bg_texture);
try GameText.initFont();
defer GameText.deinitFont();
// var slime_factory = try sf.SlimeFactory.init(allocator, renderer); // var slime_factory = try sf.SlimeFactory.init(allocator, renderer);
// defer slime_factory.deinit(); // defer slime_factory.deinit();
@ -86,13 +91,10 @@ pub fn main() !void {
} }
_ = sdl.SDL_SetRenderDrawColor(renderer, 0xff, 0xff, 0xff, 0xff); _ = sdl.SDL_SetRenderDrawColor(renderer, 0xff, 0xff, 0xff, 0xff);
_ = sdl.SDL_RenderClear(renderer); _ = sdl.SDL_RenderClear(renderer);
std.debug.print("r:{x} g:{x} b:{x}\n", .{ _ = sdl.SDL_RenderCopy(renderer, bg_texture, null, null);
game_state.r,
game_state.g,
game_state.b,
});
game_state.update_tick(); game_state.update_tick();
var text = try GameText.loadFromRenderedText("Test 123", RGBAColor.whiteSmoke().tosdl());
try text.render(&game_state);
// Render red rect // Render red rect
// const fill_rect: sdl.struct_SDL_Rect = sdl.SDL_Rect{ .x = SCREEN_WIDTH / 4, .y = SCREEN_HEIGHT / 4, .w = SCREEN_WIDTH / 2 + img_pos.x, .h = SCREEN_HEIGHT / 2 + img_pos.y }; // const fill_rect: sdl.struct_SDL_Rect = sdl.SDL_Rect{ .x = SCREEN_WIDTH / 4, .y = SCREEN_HEIGHT / 4, .w = SCREEN_WIDTH / 2 + img_pos.x, .h = SCREEN_HEIGHT / 2 + img_pos.y };
@ -105,7 +107,7 @@ pub fn main() !void {
// _ = sdl.SDL_BlitScaled(zig_image, &src_rect, screen_surface, &dest_rect); // _ = sdl.SDL_BlitScaled(zig_image, &src_rect, screen_surface, &dest_rect);
// _ = sdl.SDL_UpdateWindowSurface(window); // _ = sdl.SDL_UpdateWindowSurface(window);
sdl.SDL_Delay(117); sdl.SDL_Delay(100);
} }
return; return;
} }
@ -138,11 +140,17 @@ fn init() !void {
log("Unable to initialize SDL Image: %s\n", sdl.IMG_GetError()); log("Unable to initialize SDL Image: %s\n", sdl.IMG_GetError());
return error.SDLInitializationFailed; return error.SDLInitializationFailed;
} }
if (sdl.TTF_Init() < 0) {
log("Unable to initialize SDL TTF: %s\n", sdl.TTF_GetError());
return error.SDLInitializationFailed;
}
} }
fn close() void { fn close() void {
sdl.SDL_DestroyRenderer(renderer); sdl.SDL_DestroyRenderer(renderer);
sdl.SDL_DestroyWindow(window); sdl.SDL_DestroyWindow(window);
sdl.IMG_Quit(); sdl.IMG_Quit();
sdl.TTF_Quit();
sdl.SDL_Quit(); sdl.SDL_Quit();
} }

View File

@ -1,4 +1,5 @@
pub const c = @cImport({ pub const c = @cImport({
@cInclude("SDL2/SDL.h"); @cInclude("SDL2/SDL.h");
@cInclude("SDL2/SDL_image.h"); @cInclude("SDL2/SDL_image.h");
@cInclude("SDL2/SDL_ttf.h");
}); });

View File

@ -3,12 +3,19 @@ const sdl = @import("sdl.zig").c;
const atils = @import("./asset_utils.zig"); const atils = @import("./asset_utils.zig");
const GameState = @import("./game_state.zig").GameState; const GameState = @import("./game_state.zig").GameState;
const slime_sprite_sheet_texture_path = "assets/slime_still.png"; const slime_sprite_sheet_texture_path = "assets/slime_still.png";
const rgba = @import("./utils/rgb_color.zig").RGBAColor;
pub const Slime = struct { pub const Slime = struct {
total_frames: u8 = 6, total_frames: u8 = 6,
x: i32 = 64, x: i32 = 64,
y: i32 = 64, y: i32 = 300,
f: u16 = 0, f: u16 = 0,
color: rgba = .{
.r = 0xd5,
.g = 0x0f,
.b = 0x65,
.a = 110,
},
}; };
pub const SlimeFactory = struct { pub const SlimeFactory = struct {
@ -17,6 +24,11 @@ pub const SlimeFactory = struct {
pub fn init(allocator: std.mem.Allocator, renderer: *sdl.struct_SDL_Renderer) !SlimeFactory { pub fn init(allocator: std.mem.Allocator, renderer: *sdl.struct_SDL_Renderer) !SlimeFactory {
const texture = try atils.loadTexture(renderer, slime_sprite_sheet_texture_path); const texture = try atils.loadTexture(renderer, slime_sprite_sheet_texture_path);
const val = sdl.SDL_SetTextureBlendMode(texture, sdl.SDL_BLENDMODE_BLEND);
if (val != 0) {
sdl.SDL_Log("Failed to set blend mode: %s", sdl.SDL_GetError());
return error.FailedToSetBlendMode;
}
return .{ return .{
.slimes = std.ArrayList(Slime).init(allocator), .slimes = std.ArrayList(Slime).init(allocator),
.slime_sprite_sheet_texture = texture, .slime_sprite_sheet_texture = texture,
@ -33,12 +45,21 @@ pub const SlimeFactory = struct {
} }
pub fn render(sf: *SlimeFactory, game_state: *GameState) void { pub fn render(sf: *SlimeFactory, game_state: *GameState) void {
_ = sdl.SDL_SetTextureColorMod(sf.slime_sprite_sheet_texture, game_state.r, game_state.g, game_state.b); if (sf.slimes.items.len == 0) return;
const slime_color = sf.slimes.items[0].color;
_ = sdl.SDL_SetTextureColorMod(
sf.slime_sprite_sheet_texture,
slime_color.r,
slime_color.g,
slime_color.b,
);
_ = sdl.SDL_SetTextureAlphaMod(sf.slime_sprite_sheet_texture, slime_color.a);
for (sf.slimes.items, 0..) |slime, s_idx| { for (sf.slimes.items, 0..) |slime, s_idx| {
std.debug.print("Slime {d}:\t{}\n", .{ s_idx + 1, slime });
const sprite_frame_x_dim: u16 = slime.f * 64; const sprite_frame_x_dim: u16 = slime.f * 64;
const sprite_frame_rect: sdl.struct_SDL_Rect = .{ .x = sprite_frame_x_dim, .y = 0, .w = 64, .h = 64 }; const sprite_frame_rect: sdl.struct_SDL_Rect = .{ .x = sprite_frame_x_dim, .y = 0, .w = 64, .h = 64 };
const dest_rect: sdl.struct_SDL_Rect = .{ .x = slime.x, .y = slime.y, .w = 128, .h = 128 }; const dest_rect: sdl.struct_SDL_Rect = .{ .x = slime.x, .y = slime.y, .w = 64, .h = 64 };
_ = sdl.SDL_RenderCopy(game_state.renderer, sf.slime_sprite_sheet_texture, &sprite_frame_rect, &dest_rect); _ = sdl.SDL_RenderCopy(game_state.renderer, sf.slime_sprite_sheet_texture, &sprite_frame_rect, &dest_rect);
sf.slimes.items[s_idx].f = (sf.slimes.items[s_idx].f + 1) % sf.slimes.items[s_idx].total_frames; sf.slimes.items[s_idx].f = (sf.slimes.items[s_idx].f + 1) % sf.slimes.items[s_idx].total_frames;
} }

84
src/text.zig Normal file
View File

@ -0,0 +1,84 @@
const sdl = @import("./sdl.zig").c;
const std = @import("std");
const GameState = @import("./game_state.zig").GameState;
var font: ?*sdl.TTF_Font = null;
var text_texture: ?*sdl.SDL_Texture = null;
var text_init = false;
pub const GameText = struct {
text: []const u8,
color: sdl.SDL_Color,
surface: *sdl.SDL_Surface,
w: u32,
h: u32,
pub fn loadFromRenderedText(text: []const u8, color: sdl.SDL_Color) !GameText {
const c_text: [*c]const u8 = @ptrCast(text);
const text_surface: [*c]sdl.SDL_Surface = sdl.TTF_RenderText_Solid(font.?, c_text, color) orelse {
sdl.SDL_Log("Error loading text surface: %s\n", sdl.SDL_GetError());
return error.FailedToRenderSurface;
};
return .{
.surface = text_surface,
.text = text,
.color = color,
.w = @intCast(text_surface.*.w),
.h = @intCast(text_surface.*.h),
};
}
pub fn deinit(self: *GameText) void {
sdl.SDL_FreeSurface(self.text_surface);
}
pub fn setBlendMode(self: *GameText, blend: sdl.SDL_BlendMode) !void {
_ = self;
_ = blend;
return error.Unimplemented;
}
pub fn setAlpha(self: *GameText, alpha: u8) !void {
_ = self;
_ = alpha;
return error.Unimplemented;
}
pub fn render(self: *GameText, game_state: *GameState) !void {
if (!text_init) {
return error.TextNotInitialized;
}
if (text_texture != null) {
sdl.SDL_DestroyTexture(text_texture);
text_texture = null;
}
// TODO dont create new texture if unchanged
text_texture = sdl.SDL_CreateTextureFromSurface(game_state.renderer, self.surface) orelse {
sdl.SDL_Log("Error loading text texture: %s\n", sdl.SDL_GetError());
return error.FailedToRenderTexture;
};
const srcr = sdl.SDL_Rect{ .x = 0, .y = 0, .w = @intCast(self.w), .h = @intCast(self.h) };
const destr = sdl.SDL_Rect{ .x = @intCast(game_state.SCREEN_WIDTH - self.w), .y = 10, .w = @intCast(self.w), .h = @intCast(self.h) };
_ = sdl.SDL_RenderCopy(game_state.renderer, text_texture, &srcr, &destr);
}
pub fn initFont() !void {
const loaded_font = sdl.TTF_OpenFont("./assets/fonts/DepartureMonoNF-Regular.ttf", 24) orelse {
sdl.SDL_Log("Error loading ttf font: %s\n", sdl.TTF_GetError());
return error.FailedToLoadFont;
};
font = loaded_font;
text_init = true;
}
pub fn deinitFont() void {
sdl.SDL_DestroyTexture(text_texture);
sdl.TTF_CloseFont(font);
text_init = false;
}
};

116
src/utils/rgb_color.zig Normal file
View File

@ -0,0 +1,116 @@
const sdl = @import("../sdl.zig").c;
pub const RGBAColor = struct {
r: u8,
g: u8,
b: u8,
a: u8,
pub fn white() RGBAColor {
return .{
.r = 0xff,
.g = 0xff,
.b = 0xff,
.a = 0xff,
};
}
pub fn black() RGBAColor {
return .{
.r = 0x00,
.g = 0x00,
.b = 0x00,
.a = 0xff,
};
}
pub fn red() RGBAColor {
return .{
.r = 0xff,
.g = 0x00,
.b = 0x00,
.a = 0xff,
};
}
pub fn green() RGBAColor {
return .{
.r = 0x00,
.g = 0xff,
.b = 0x00,
.a = 0xff,
};
}
pub fn blue() RGBAColor {
return .{
.r = 0x00,
.g = 0x00,
.b = 0xff,
.a = 0xff,
};
}
pub fn yellow() RGBAColor {
return .{
.r = 0xff,
.g = 0xff,
.b = 0x00,
.a = 0xff,
};
}
pub fn cyan() RGBAColor {
return .{
.r = 0x00,
.g = 0xff,
.b = 0xff,
.a = 0xff,
};
}
pub fn magenta() RGBAColor {
return .{
.r = 0xff,
.g = 0x00,
.b = 0xff,
.a = 0xff,
};
}
pub fn whiteSmoke() RGBAColor {
return .{
.r = 0xf5,
.g = 0xf5,
.b = 0xf5,
.a = 0xff,
};
}
pub fn orange() RGBAColor {
return .{
.r = 0xff,
.g = 0xa5,
.b = 0x00,
.a = 0xff,
};
}
pub fn pink() RGBAColor {
return .{
.r = 0xff,
.g = 0xc0,
.b = 0xcb,
.a = 0xff,
};
}
pub fn tosdl(self: *const RGBAColor) sdl.SDL_Color {
return .{
.r = self.r,
.g = self.g,
.b = self.b,
.a = self.a,
};
}
};