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 assert = std.debug.assert;
|
||||||
const PlayerInput = @import("player-input.zig");
|
const PlayerInput = @import("player-input.zig");
|
||||||
const UI = @import("./ui.zig");
|
const UI = @import("./ui.zig");
|
||||||
|
const PixelPerfect = @import("./pixel-perfect.zig");
|
||||||
|
const UIBox = @import("./ui-box.zig");
|
||||||
|
|
||||||
const friction = 0.99;
|
const friction = 0.99;
|
||||||
const walkForce = 4000;
|
const walkForce = 4000;
|
||||||
@ -11,6 +13,8 @@ const maxSpeed = 200;
|
|||||||
const handDistance = 50;
|
const handDistance = 50;
|
||||||
const deathBallSize: f32 = 10;
|
const deathBallSize: f32 = 10;
|
||||||
const playerSize = 20;
|
const playerSize = 20;
|
||||||
|
const virtualWidth = 300;
|
||||||
|
const virtualHeight = 300;
|
||||||
|
|
||||||
const enemyFriction = friction;
|
const enemyFriction = friction;
|
||||||
|
|
||||||
@ -204,6 +208,7 @@ rope: Rope,
|
|||||||
ui: UI,
|
ui: UI,
|
||||||
should_close: bool = false,
|
should_close: bool = false,
|
||||||
paused: bool = false,
|
paused: bool = false,
|
||||||
|
pixel_effect: PixelPerfect,
|
||||||
|
|
||||||
camera: rl.Camera2D,
|
camera: rl.Camera2D,
|
||||||
|
|
||||||
@ -223,7 +228,8 @@ pub fn init(allocator: Allocator) Self {
|
|||||||
.player = player,
|
.player = player,
|
||||||
.rope = Rope.init(handPosition, handPosition.add(.{ .x = ropeSize, .y = 0.002 }), 10),
|
.rope = Rope.init(handPosition, handPosition.add(.{ .x = ropeSize, .y = 0.002 }), 10),
|
||||||
.camera = .{ .target = playerPosition },
|
.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 {
|
pub fn deinit(self: *Self) void {
|
||||||
self.player.input.deactivate();
|
self.player.input.deactivate();
|
||||||
self.enemies.deinit();
|
self.enemies.deinit();
|
||||||
|
self.pixel_effect.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tickPlayer(self: *Self) void {
|
fn tickPlayer(self: *Self) void {
|
||||||
@ -384,82 +391,19 @@ fn allocDrawText(
|
|||||||
rl.DrawText(text, x, y, font_size, color);
|
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 {
|
fn tickUI(self: *Self) !void {
|
||||||
|
self.ui.mouse_position = self.pixel_effect.getMousePosition();
|
||||||
|
self.ui.mouse_delta = self.pixel_effect.getMouseDelta();
|
||||||
|
|
||||||
self.ui.begin();
|
self.ui.begin();
|
||||||
defer self.ui.end();
|
defer self.ui.end();
|
||||||
|
|
||||||
var allocator = self.allocator;
|
var allocator = self.allocator;
|
||||||
const screen_box = UIBox.initScreen();
|
const screen_box = UIBox.init(0 ,0, virtualWidth, virtualHeight);
|
||||||
const font = rl.GetFontDefault();
|
const font = rl.GetFontDefault();
|
||||||
|
|
||||||
try allocDrawText(allocator, "{d}", .{self.spawnedEnemiesCount}, 10, 10, 24, rl.RED);
|
try allocDrawText(allocator, "{d}", .{self.spawnedEnemiesCount}, 10, 10, 12, rl.RED);
|
||||||
try allocDrawText(allocator, "{d}", .{self.killCount}, 10, 30, 24, rl.GREEN);
|
try allocDrawText(allocator, "{d}", .{self.killCount}, 10, 30, 12, rl.GREEN);
|
||||||
|
|
||||||
const minutes_text = try std.fmt.allocPrint(allocator, "{d:.0}", .{self.timePassed/60});
|
const minutes_text = try std.fmt.allocPrint(allocator, "{d:.0}", .{self.timePassed/60});
|
||||||
defer allocator.free(minutes_text);
|
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);
|
const minutes_text_z = try std.mem.concatWithSentinel(allocator, u8, &.{ minutes_text }, 0);
|
||||||
defer allocator.free(minutes_text_z);
|
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_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 });
|
const time_passed_pos = screen_box.center_top().add(.{ .x = -time_passed_width/2, .y = 30 });
|
||||||
rl.DrawTextEx(
|
rl.DrawTextEx(
|
||||||
@ -481,7 +425,7 @@ fn tickUI(self: *Self) !void {
|
|||||||
time_passed_text,
|
time_passed_text,
|
||||||
time_passed_pos,
|
time_passed_pos,
|
||||||
font_size,
|
font_size,
|
||||||
4.8,
|
font_size/10,
|
||||||
rl.GREEN
|
rl.GREEN
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -590,13 +534,11 @@ fn drawPlayer(self: *Self) void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn tick(self: *Self) !void {
|
pub fn tick(self: *Self) !void {
|
||||||
rl.ClearBackground(rl.BROWN);
|
|
||||||
|
|
||||||
const screenSize = getScreenSize();
|
|
||||||
const dt = rl.GetFrameTime();
|
const dt = rl.GetFrameTime();
|
||||||
|
|
||||||
self.camera.offset.x = screenSize.x/2;
|
self.camera.offset.x = virtualWidth/2;
|
||||||
self.camera.offset.y = screenSize.y/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);
|
self.camera.target = rl.Vector2Lerp(self.camera.target, self.player.position, 10 * dt);
|
||||||
|
|
||||||
var enemies = &self.enemies;
|
var enemies = &self.enemies;
|
||||||
@ -617,6 +559,8 @@ pub fn tick(self: *Self) !void {
|
|||||||
|
|
||||||
const deathBallPosition = rope.nodePositions.get(rope.nodePositions.len-1);
|
const deathBallPosition = rope.nodePositions.get(rope.nodePositions.len-1);
|
||||||
|
|
||||||
|
self.pixel_effect.begin();
|
||||||
|
rl.ClearBackground(rl.BROWN);
|
||||||
rl.BeginMode2D(self.camera);
|
rl.BeginMode2D(self.camera);
|
||||||
{
|
{
|
||||||
rl.DrawCircle(0, 0, 5, rl.GOLD);
|
rl.DrawCircle(0, 0, 5, rl.GOLD);
|
||||||
@ -667,6 +611,9 @@ pub fn tick(self: *Self) !void {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
rl.EndMode2D();
|
rl.EndMode2D();
|
||||||
|
self.pixel_effect.end();
|
||||||
|
|
||||||
|
self.pixel_effect.beginTransform();
|
||||||
try self.tickUI();
|
try self.tickUI();
|
||||||
|
self.pixel_effect.endTransform();
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,9 @@ pub fn main() !void {
|
|||||||
defer _ = gpa.deinit();
|
defer _ = gpa.deinit();
|
||||||
|
|
||||||
rl.SetTargetFPS(60);
|
rl.SetTargetFPS(60);
|
||||||
|
rl.SetConfigFlags(rl.ConfigFlags{
|
||||||
|
.FLAG_WINDOW_RESIZABLE = true,
|
||||||
|
});
|
||||||
rl.InitWindow(1200, 1200, "Step kill");
|
rl.InitWindow(1200, 1200, "Step kill");
|
||||||
rl.SetExitKey(.KEY_NULL);
|
rl.SetExitKey(.KEY_NULL);
|
||||||
defer rl.CloseWindow();
|
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,
|
is_released: bool = false,
|
||||||
first_render: bool = true,
|
first_render: bool = true,
|
||||||
|
|
||||||
|
mouse_position: rl.Vector2 = rl.Vector2.zero(),
|
||||||
|
mouse_delta: rl.Vector2 = rl.Vector2.zero(),
|
||||||
|
|
||||||
pub fn init() @This() {
|
pub fn init() @This() {
|
||||||
return @This(){};
|
return @This(){};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn didMouseMove() bool {
|
fn didMouseMove(self: *@This()) bool {
|
||||||
const mouse_delta = rl.GetMouseDelta();
|
return (self.mouse_delta.x != 0 or self.mouse_delta.y != 0) and !rl.IsCursorHidden();
|
||||||
return (mouse_delta.x != 0 or 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 {
|
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.last_button_idx += 1;
|
||||||
self.button_count += 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.selected_button = @intCast(button_idx);
|
||||||
self.was_mouse_hot = true;
|
self.was_mouse_hot = true;
|
||||||
}
|
}
|
||||||
@ -97,7 +104,7 @@ pub fn begin(self: *@This()) void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn end(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;
|
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);
|
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