add pixel art effect
This commit is contained in:
parent
f2545d27e9
commit
2941d9a39c
@ -4,6 +4,8 @@ const Allocator = std.mem.Allocator;
|
||||
const assert = std.debug.assert;
|
||||
const PlayerInput = @import("player-input.zig");
|
||||
const UI = @import("./ui.zig");
|
||||
const PixelPerfect = @import("./pixel-perfect.zig");
|
||||
const UIBox = @import("./ui-box.zig");
|
||||
|
||||
const friction = 0.99;
|
||||
const walkForce = 4000;
|
||||
@ -11,6 +13,8 @@ const maxSpeed = 200;
|
||||
const handDistance = 50;
|
||||
const deathBallSize: f32 = 10;
|
||||
const playerSize = 20;
|
||||
const virtualWidth = 300;
|
||||
const virtualHeight = 300;
|
||||
|
||||
const enemyFriction = friction;
|
||||
|
||||
@ -204,6 +208,7 @@ rope: Rope,
|
||||
ui: UI,
|
||||
should_close: bool = false,
|
||||
paused: bool = false,
|
||||
pixel_effect: PixelPerfect,
|
||||
|
||||
camera: rl.Camera2D,
|
||||
|
||||
@ -223,7 +228,8 @@ pub fn init(allocator: Allocator) Self {
|
||||
.player = player,
|
||||
.rope = Rope.init(handPosition, handPosition.add(.{ .x = ropeSize, .y = 0.002 }), 10),
|
||||
.camera = .{ .target = playerPosition },
|
||||
.ui = UI.init()
|
||||
.ui = UI.init(),
|
||||
.pixel_effect = PixelPerfect.init(virtualWidth, virtualHeight)
|
||||
};
|
||||
}
|
||||
|
||||
@ -235,6 +241,7 @@ pub fn reset(self: *Self) void {
|
||||
pub fn deinit(self: *Self) void {
|
||||
self.player.input.deactivate();
|
||||
self.enemies.deinit();
|
||||
self.pixel_effect.deinit();
|
||||
}
|
||||
|
||||
fn tickPlayer(self: *Self) void {
|
||||
@ -384,82 +391,19 @@ fn allocDrawText(
|
||||
rl.DrawText(text, x, y, font_size, color);
|
||||
}
|
||||
|
||||
const UIBox = struct {
|
||||
x: f32,
|
||||
y: f32,
|
||||
width: f32,
|
||||
height: f32,
|
||||
|
||||
fn init(x: f32, y: f32, width: f32, height: f32) UIBox {
|
||||
return UIBox{
|
||||
.x = x,
|
||||
.y = y,
|
||||
.width = width,
|
||||
.height = height,
|
||||
};
|
||||
}
|
||||
|
||||
fn initScreen() UIBox {
|
||||
const width: f32 = @floatFromInt(rl.GetScreenWidth());
|
||||
const height: f32 = @floatFromInt(rl.GetScreenHeight());
|
||||
return UIBox.init(0, 0, width, height);
|
||||
}
|
||||
|
||||
fn box(self: UIBox, x: f32, y: f32, width: f32, height: f32) UIBox {
|
||||
return UIBox.init(self.x + x, self.y + y, width, height);
|
||||
}
|
||||
|
||||
fn rect(self: UIBox) rl.Rectangle {
|
||||
return rl.Rectangle{
|
||||
.x = self.x,
|
||||
.y = self.y,
|
||||
.width = self.width,
|
||||
.height = self.height,
|
||||
};
|
||||
}
|
||||
|
||||
fn left(self: UIBox) f32 {
|
||||
return self.x;
|
||||
}
|
||||
fn center(self: UIBox) f32 {
|
||||
return self.x + self.width/2;
|
||||
}
|
||||
fn right(self: UIBox) f32 {
|
||||
return self.x + self.width;
|
||||
}
|
||||
|
||||
fn top(self: UIBox) f32 {
|
||||
return self.y;
|
||||
}
|
||||
fn middle(self: UIBox) f32 {
|
||||
return self.y + self.height/2;
|
||||
}
|
||||
fn bottom(self: UIBox) f32 {
|
||||
return self.y + self.height;
|
||||
}
|
||||
|
||||
fn center_top(self: UIBox) rl.Vector2 {
|
||||
return rl.Vector2{ .x = self.center(), .y = self.top() };
|
||||
}
|
||||
fn center_middle(self: UIBox) rl.Vector {
|
||||
return rl.Vector2{ .x = self.center(), .y = self.middle() };
|
||||
}
|
||||
|
||||
fn margin(self: UIBox, amount: f32) UIBox {
|
||||
return UIBox.init(self.x + amount, self.y + amount, self.width - 2*amount, self.height - 2*amount);
|
||||
}
|
||||
};
|
||||
|
||||
fn tickUI(self: *Self) !void {
|
||||
self.ui.mouse_position = self.pixel_effect.getMousePosition();
|
||||
self.ui.mouse_delta = self.pixel_effect.getMouseDelta();
|
||||
|
||||
self.ui.begin();
|
||||
defer self.ui.end();
|
||||
|
||||
var allocator = self.allocator;
|
||||
const screen_box = UIBox.initScreen();
|
||||
const screen_box = UIBox.init(0 ,0, virtualWidth, virtualHeight);
|
||||
const font = rl.GetFontDefault();
|
||||
|
||||
try allocDrawText(allocator, "{d}", .{self.spawnedEnemiesCount}, 10, 10, 24, rl.RED);
|
||||
try allocDrawText(allocator, "{d}", .{self.killCount}, 10, 30, 24, rl.GREEN);
|
||||
try allocDrawText(allocator, "{d}", .{self.spawnedEnemiesCount}, 10, 10, 12, rl.RED);
|
||||
try allocDrawText(allocator, "{d}", .{self.killCount}, 10, 30, 12, rl.GREEN);
|
||||
|
||||
const minutes_text = try std.fmt.allocPrint(allocator, "{d:.0}", .{self.timePassed/60});
|
||||
defer allocator.free(minutes_text);
|
||||
@ -473,7 +417,7 @@ fn tickUI(self: *Self) !void {
|
||||
const minutes_text_z = try std.mem.concatWithSentinel(allocator, u8, &.{ minutes_text }, 0);
|
||||
defer allocator.free(minutes_text_z);
|
||||
|
||||
const font_size = 48;
|
||||
const font_size = 12;
|
||||
const time_passed_width: f32 = @floatFromInt(rl.MeasureText(minutes_text_z, font_size) + rl.MeasureText(":000", font_size));
|
||||
const time_passed_pos = screen_box.center_top().add(.{ .x = -time_passed_width/2, .y = 30 });
|
||||
rl.DrawTextEx(
|
||||
@ -481,7 +425,7 @@ fn tickUI(self: *Self) !void {
|
||||
time_passed_text,
|
||||
time_passed_pos,
|
||||
font_size,
|
||||
4.8,
|
||||
font_size/10,
|
||||
rl.GREEN
|
||||
);
|
||||
|
||||
@ -590,13 +534,11 @@ fn drawPlayer(self: *Self) void {
|
||||
}
|
||||
|
||||
pub fn tick(self: *Self) !void {
|
||||
rl.ClearBackground(rl.BROWN);
|
||||
|
||||
const screenSize = getScreenSize();
|
||||
const dt = rl.GetFrameTime();
|
||||
|
||||
self.camera.offset.x = screenSize.x/2;
|
||||
self.camera.offset.y = screenSize.y/2;
|
||||
self.camera.offset.x = virtualWidth/2;
|
||||
self.camera.offset.y = virtualHeight/2;
|
||||
self.camera.zoom = 0.4;
|
||||
self.camera.target = rl.Vector2Lerp(self.camera.target, self.player.position, 10 * dt);
|
||||
|
||||
var enemies = &self.enemies;
|
||||
@ -617,6 +559,8 @@ pub fn tick(self: *Self) !void {
|
||||
|
||||
const deathBallPosition = rope.nodePositions.get(rope.nodePositions.len-1);
|
||||
|
||||
self.pixel_effect.begin();
|
||||
rl.ClearBackground(rl.BROWN);
|
||||
rl.BeginMode2D(self.camera);
|
||||
{
|
||||
rl.DrawCircle(0, 0, 5, rl.GOLD);
|
||||
@ -667,6 +611,9 @@ pub fn tick(self: *Self) !void {
|
||||
);
|
||||
}
|
||||
rl.EndMode2D();
|
||||
self.pixel_effect.end();
|
||||
|
||||
self.pixel_effect.beginTransform();
|
||||
try self.tickUI();
|
||||
self.pixel_effect.endTransform();
|
||||
}
|
||||
|
@ -9,6 +9,9 @@ pub fn main() !void {
|
||||
defer _ = gpa.deinit();
|
||||
|
||||
rl.SetTargetFPS(60);
|
||||
rl.SetConfigFlags(rl.ConfigFlags{
|
||||
.FLAG_WINDOW_RESIZABLE = true,
|
||||
});
|
||||
rl.InitWindow(1200, 1200, "Step kill");
|
||||
rl.SetExitKey(.KEY_NULL);
|
||||
defer rl.CloseWindow();
|
||||
|
102
src/pixel-perfect.zig
Normal file
102
src/pixel-perfect.zig
Normal file
@ -0,0 +1,102 @@
|
||||
const rl = @import("raylib");
|
||||
|
||||
target: rl.RenderTexture2D,
|
||||
|
||||
pub fn init(width: u32, height: u32) @This() {
|
||||
return @This(){
|
||||
.target = rl.LoadRenderTexture(@intCast(width), @intCast(height))
|
||||
};
|
||||
}
|
||||
|
||||
fn getScale(self: @This()) f32 {
|
||||
const screen_width: f32 = @floatFromInt(rl.GetScreenWidth());
|
||||
const screen_height: f32 = @floatFromInt(rl.GetScreenHeight());
|
||||
const virtual_width: f32 = @floatFromInt(self.target.texture.width);
|
||||
const virtual_height: f32 = @floatFromInt(self.target.texture.height);
|
||||
|
||||
const width_scale = screen_width / virtual_width;
|
||||
const height_scale = screen_height / virtual_height;
|
||||
const scale = @min(width_scale, height_scale);
|
||||
|
||||
return scale;
|
||||
}
|
||||
|
||||
fn getOffset(self: @This()) rl.Vector2 {
|
||||
const screen_width: f32 = @floatFromInt(rl.GetScreenWidth());
|
||||
const screen_height: f32 = @floatFromInt(rl.GetScreenHeight());
|
||||
const virtual_width: f32 = @floatFromInt(self.target.texture.width);
|
||||
const virtual_height: f32 = @floatFromInt(self.target.texture.height);
|
||||
|
||||
const scale = self.getScale();
|
||||
|
||||
return rl.Vector2{
|
||||
.x = (screen_width - virtual_width*scale)/2,
|
||||
.y = (screen_height - virtual_height*scale)/2
|
||||
};
|
||||
}
|
||||
|
||||
pub fn toWorldSpace(self: *@This(), pos: rl.Vector2) rl.Vector2 {
|
||||
const offset = self.getOffset();
|
||||
const scale = self.getScale();
|
||||
|
||||
return rl.Vector2{
|
||||
.x = (pos.x - offset.x) / scale,
|
||||
.y = (pos.y - offset.y) / scale
|
||||
};
|
||||
}
|
||||
|
||||
pub fn getMousePosition(self: *@This()) rl.Vector2 {
|
||||
return self.toWorldSpace(rl.GetMousePosition());
|
||||
}
|
||||
|
||||
pub fn getMouseDelta(self: *@This()) rl.Vector2 {
|
||||
const scale = self.getScale();
|
||||
return rl.GetMouseDelta().scale(scale);
|
||||
}
|
||||
|
||||
pub fn deinit(self: @This()) void {
|
||||
rl.UnloadRenderTexture(self.target);
|
||||
}
|
||||
|
||||
pub fn begin(self: *@This()) void {
|
||||
rl.ClearBackground(rl.BLACK);
|
||||
rl.BeginTextureMode(self.target);
|
||||
}
|
||||
|
||||
pub fn end(self: *@This()) void {
|
||||
rl.EndTextureMode();
|
||||
|
||||
const offset = self.getOffset();
|
||||
const scale = self.getScale();
|
||||
|
||||
const virtual_width: f32 = @floatFromInt(self.target.texture.width);
|
||||
const virtual_height: f32 = @floatFromInt(self.target.texture.height);
|
||||
|
||||
const target_source = rl.Rectangle{
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = virtual_width,
|
||||
.height = -virtual_height,
|
||||
};
|
||||
const target_dest = rl.Rectangle{
|
||||
.x = offset.x,
|
||||
.y = offset.y,
|
||||
.width = virtual_width*scale,
|
||||
.height = virtual_height*scale,
|
||||
};
|
||||
rl.DrawTexturePro(self.target.texture, target_source, target_dest, rl.Vector2.zero(), 0, rl.WHITE);
|
||||
}
|
||||
|
||||
pub fn beginTransform(self: *@This()) void {
|
||||
const offset = self.getOffset();
|
||||
const scale = self.getScale();
|
||||
|
||||
rl.rlPushMatrix();
|
||||
rl.rlTranslatef(offset.x, offset.y, 0);
|
||||
rl.rlScalef(scale, scale, 1);
|
||||
}
|
||||
|
||||
pub fn endTransform(self: *@This()) void {
|
||||
rl.rlPopMatrix();
|
||||
_ = self;
|
||||
}
|
65
src/ui-box.zig
Normal file
65
src/ui-box.zig
Normal file
@ -0,0 +1,65 @@
|
||||
const rl = @import("raylib");
|
||||
|
||||
x: f32,
|
||||
y: f32,
|
||||
width: f32,
|
||||
height: f32,
|
||||
|
||||
pub fn init(x: f32, y: f32, width: f32, height: f32) @This() {
|
||||
return @This(){
|
||||
.x = x,
|
||||
.y = y,
|
||||
.width = width,
|
||||
.height = height,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn initScreen() @This() {
|
||||
const width: f32 = @floatFromInt(rl.GetScreenWidth());
|
||||
const height: f32 = @floatFromInt(rl.GetScreenHeight());
|
||||
return @This().init(0, 0, width, height);
|
||||
}
|
||||
|
||||
pub fn box(self: @This(), x: f32, y: f32, width: f32, height: f32) @This() {
|
||||
return @This().init(self.x + x, self.y + y, width, height);
|
||||
}
|
||||
|
||||
pub fn rect(self: @This()) rl.Rectangle {
|
||||
return rl.Rectangle{
|
||||
.x = self.x,
|
||||
.y = self.y,
|
||||
.width = self.width,
|
||||
.height = self.height,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn left(self: @This()) f32 {
|
||||
return self.x;
|
||||
}
|
||||
pub fn center(self: @This()) f32 {
|
||||
return self.x + self.width/2;
|
||||
}
|
||||
pub fn right(self: @This()) f32 {
|
||||
return self.x + self.width;
|
||||
}
|
||||
|
||||
pub fn top(self: @This()) f32 {
|
||||
return self.y;
|
||||
}
|
||||
pub fn middle(self: @This()) f32 {
|
||||
return self.y + self.height/2;
|
||||
}
|
||||
pub fn bottom(self: @This()) f32 {
|
||||
return self.y + self.height;
|
||||
}
|
||||
|
||||
pub fn center_top(self: @This()) rl.Vector2 {
|
||||
return rl.Vector2{ .x = self.center(), .y = self.top() };
|
||||
}
|
||||
pub fn center_middle(self: @This()) rl.Vector {
|
||||
return rl.Vector2{ .x = self.center(), .y = self.middle() };
|
||||
}
|
||||
|
||||
pub fn margin(self: @This(), amount: f32) @This() {
|
||||
return @This().init(self.x + amount, self.y + amount, self.width - 2*amount, self.height - 2*amount);
|
||||
}
|
21
src/ui.zig
21
src/ui.zig
@ -12,13 +12,20 @@ is_down: bool = false,
|
||||
is_released: bool = false,
|
||||
first_render: bool = true,
|
||||
|
||||
mouse_position: rl.Vector2 = rl.Vector2.zero(),
|
||||
mouse_delta: rl.Vector2 = rl.Vector2.zero(),
|
||||
|
||||
pub fn init() @This() {
|
||||
return @This(){};
|
||||
}
|
||||
|
||||
fn didMouseMove() bool {
|
||||
const mouse_delta = rl.GetMouseDelta();
|
||||
return (mouse_delta.x != 0 or mouse_delta.y != 0) and !rl.IsCursorHidden();
|
||||
fn didMouseMove(self: *@This()) bool {
|
||||
return (self.mouse_delta.x != 0 or self.mouse_delta.y != 0) and !rl.IsCursorHidden();
|
||||
}
|
||||
|
||||
fn isMouseInBounds(self: *@This(), x: f32, y: f32, width: f32, height: f32) bool {
|
||||
const pos = self.mouse_position;
|
||||
return (x <= pos.x and pos.x <= x + width) and (y <= pos.y and pos.y <= y + height);
|
||||
}
|
||||
|
||||
pub fn button(self: *@This(), text: [:0]const u8, x: f32, y: f32, width: f32, height: f32) bool {
|
||||
@ -26,7 +33,7 @@ pub fn button(self: *@This(), text: [:0]const u8, x: f32, y: f32, width: f32, he
|
||||
self.last_button_idx += 1;
|
||||
self.button_count += 1;
|
||||
|
||||
if (didMouseMove() and isMouseInBounds(x, y, width, height)) {
|
||||
if (self.didMouseMove() and self.isMouseInBounds(x, y, width, height)) {
|
||||
self.selected_button = @intCast(button_idx);
|
||||
self.was_mouse_hot = true;
|
||||
}
|
||||
@ -97,7 +104,7 @@ pub fn begin(self: *@This()) void {
|
||||
}
|
||||
|
||||
pub fn end(self: *@This()) void {
|
||||
if (!self.was_mouse_hot and didMouseMove()) {
|
||||
if (!self.was_mouse_hot and self.didMouseMove()) {
|
||||
self.selected_button = -1;
|
||||
}
|
||||
}
|
||||
@ -115,7 +122,3 @@ pub fn drawTextAligned(
|
||||
rl.DrawTextEx(font, text, position.sub(text_size.scale(0.5)), font_size, spacing, tint);
|
||||
}
|
||||
|
||||
fn isMouseInBounds(x: f32, y: f32, width: f32, height: f32) bool {
|
||||
const pos = rl.GetMousePosition();
|
||||
return (x <= pos.x and pos.x <= x + width) and (y <= pos.y and pos.y <= y + height);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user