add basic button ui
This commit is contained in:
parent
28b5a476bf
commit
f7e3698cb1
@ -3,6 +3,7 @@ const std = @import("std");
|
|||||||
const Allocator = std.mem.Allocator;
|
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 friction = 0.99;
|
const friction = 0.99;
|
||||||
const walkForce = 4000;
|
const walkForce = 4000;
|
||||||
@ -51,12 +52,20 @@ const Enemy = struct {
|
|||||||
return Enemy{
|
return Enemy{
|
||||||
.position = position,
|
.position = position,
|
||||||
.health = 5.0,
|
.health = 5.0,
|
||||||
.color = rl.ORANGE,
|
.color = rl.GOLD,
|
||||||
.size = 50.0,
|
.size = 50.0,
|
||||||
.acceleration = 3000,
|
.acceleration = 3000,
|
||||||
.maxSpeed = 50,
|
.maxSpeed = 50,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn vulnerable(self: Enemy) bool {
|
||||||
|
return self.invincibility == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn alive(self: Enemy) bool {
|
||||||
|
return self.health > 0;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const Player = struct {
|
const Player = struct {
|
||||||
@ -64,6 +73,24 @@ const Player = struct {
|
|||||||
velocity: rl.Vector2 = rl.Vector2.zero(),
|
velocity: rl.Vector2 = rl.Vector2.zero(),
|
||||||
acceleration: rl.Vector2 = rl.Vector2.zero(),
|
acceleration: rl.Vector2 = rl.Vector2.zero(),
|
||||||
handDirection: rl.Vector2 = rl.Vector2{ .x = 1, .y = 0 },
|
handDirection: rl.Vector2 = rl.Vector2{ .x = 1, .y = 0 },
|
||||||
|
|
||||||
|
input: PlayerInput = PlayerInput.init(),
|
||||||
|
|
||||||
|
health: u32 = 3,
|
||||||
|
dead: bool = false,
|
||||||
|
invincibility: f32 = 0,
|
||||||
|
|
||||||
|
fn getHandPosition(self: *const Player) rl.Vector2 {
|
||||||
|
return self.position.add(self.handDirection.scale(handDistance));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn alive(self: Player) bool {
|
||||||
|
return self.health > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn vulnerable(self: Player) bool {
|
||||||
|
return self.invincibility == 0;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const Rope = struct {
|
const Rope = struct {
|
||||||
@ -153,6 +180,14 @@ const Rope = struct {
|
|||||||
pos.* = pos.add(offset.scale(0.01));
|
pos.* = pos.add(offset.scale(0.01));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn lastNodePosition(self: *Rope) rl.Vector2 {
|
||||||
|
return self.nodePositions.get(self.nodePositions.len-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lastNodeVelocity(self: *Rope) rl.Vector2 {
|
||||||
|
return self.nodeVelocities.get(self.nodeVelocities.len-1);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
allocator: Allocator,
|
allocator: Allocator,
|
||||||
@ -161,9 +196,12 @@ prng: std.rand.DefaultPrng,
|
|||||||
enemyTimer: f32 = 0,
|
enemyTimer: f32 = 0,
|
||||||
spawnedEnemiesCount: u32 = 0,
|
spawnedEnemiesCount: u32 = 0,
|
||||||
killCount: u32 = 0,
|
killCount: u32 = 0,
|
||||||
|
timePassed: f32 = 0,
|
||||||
enemies: std.ArrayList(Enemy),
|
enemies: std.ArrayList(Enemy),
|
||||||
player: Player,
|
player: Player,
|
||||||
rope: Rope,
|
rope: Rope,
|
||||||
|
ui: UI,
|
||||||
|
should_close: bool = false,
|
||||||
|
|
||||||
camera: rl.Camera2D,
|
camera: rl.Camera2D,
|
||||||
|
|
||||||
@ -173,14 +211,17 @@ pub fn init(allocator: Allocator) Self {
|
|||||||
var playerPosition = rl.Vector2{ .x = 0, .y = 0 };
|
var playerPosition = rl.Vector2{ .x = 0, .y = 0 };
|
||||||
var ropeSize: f32 = 200;
|
var ropeSize: f32 = 200;
|
||||||
var handPosition = playerPosition.add(rl.Vector2{ .x = handDistance, .y = 0});
|
var handPosition = playerPosition.add(rl.Vector2{ .x = handDistance, .y = 0});
|
||||||
|
var player = Player{ .position = playerPosition };
|
||||||
|
player.input.activate();
|
||||||
|
|
||||||
return Self {
|
return Self {
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
.prng = std.rand.DefaultPrng.init(@bitCast(std.time.timestamp())),
|
.prng = std.rand.DefaultPrng.init(@bitCast(std.time.timestamp())),
|
||||||
.enemies = std.ArrayList(Enemy).init(allocator),
|
.enemies = std.ArrayList(Enemy).init(allocator),
|
||||||
.player = .{ .position = playerPosition },
|
.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()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,10 +230,40 @@ pub fn reset(self: *Self) void {
|
|||||||
self.* = Self.init(self.allocator);
|
self.* = Self.init(self.allocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: Self) void {
|
pub fn deinit(self: *Self) void {
|
||||||
|
self.player.input.deactivate();
|
||||||
self.enemies.deinit();
|
self.enemies.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn tickPlayer(self: *Self) void {
|
||||||
|
const dt = rl.GetFrameTime();
|
||||||
|
var player = &self.player;
|
||||||
|
var rope = &self.rope;
|
||||||
|
|
||||||
|
player.acceleration = rl.Vector2.zero();
|
||||||
|
|
||||||
|
if (player.alive()) {
|
||||||
|
player.handDirection = player.input.getHandPosition();
|
||||||
|
|
||||||
|
const moveDir = player.input.getWalkDirection();
|
||||||
|
player.acceleration = moveDir;
|
||||||
|
player.acceleration = rl.Vector2Scale(player.acceleration, walkForce);
|
||||||
|
}
|
||||||
|
|
||||||
|
player.velocity = rl.Vector2Add(player.velocity, rl.Vector2Scale(player.acceleration, dt));
|
||||||
|
player.velocity = rl.Vector2ClampValue(player.velocity, 0, maxSpeed);
|
||||||
|
player.velocity = rl.Vector2Scale(player.velocity, std.math.pow(f32, (1 - friction), dt));
|
||||||
|
|
||||||
|
player.position = rl.Vector2Add(player.position, rl.Vector2Scale(player.velocity, dt));
|
||||||
|
|
||||||
|
const handPosition = player.getHandPosition();
|
||||||
|
|
||||||
|
const rope_root_node: rl.Vector2 = rope.nodePositions.get(0);
|
||||||
|
rope.nodePositions.set(0, rope_root_node.lerp(handPosition, 0.25));
|
||||||
|
|
||||||
|
rope.update(dt);
|
||||||
|
}
|
||||||
|
|
||||||
fn tickEnemies(self: *Self) !void {
|
fn tickEnemies(self: *Self) !void {
|
||||||
var rng = self.prng.random();
|
var rng = self.prng.random();
|
||||||
const dt = rl.GetFrameTime();
|
const dt = rl.GetFrameTime();
|
||||||
@ -205,21 +276,41 @@ fn tickEnemies(self: *Self) !void {
|
|||||||
self.enemyTimer = 0.5 + rng.float(f32) * 2;
|
self.enemyTimer = 0.5 + rng.float(f32) * 2;
|
||||||
self.spawnedEnemiesCount += 1;
|
self.spawnedEnemiesCount += 1;
|
||||||
const enemyPosition = rl.Vector2.randomOnUnitCircle(rng).scale(400);
|
const enemyPosition = rl.Vector2.randomOnUnitCircle(rng).scale(400);
|
||||||
try enemies.append(Enemy.initNormal(enemyPosition));
|
try enemies.append(Enemy.initStrong(enemyPosition));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const deathBallPosition = rope.lastNodePosition();
|
||||||
|
|
||||||
for (enemies.items) |*enemy| {
|
for (enemies.items) |*enemy| {
|
||||||
const toPlayer = player.position.sub(enemy.position);
|
const toPlayer = player.position.sub(enemy.position);
|
||||||
if (toPlayer.length() <= playerSize + enemy.size) {
|
if (player.vulnerable() and toPlayer.length() <= playerSize + enemy.size) {
|
||||||
self.reset();
|
self.damagePlayer();
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (enemy.vulnerable() and rl.Vector2Distance(enemy.position, deathBallPosition) < enemy.size + deathBallSize) {
|
||||||
|
self.damageEnemy(enemy);
|
||||||
|
}
|
||||||
|
|
||||||
|
var acceleration = rl.Vector2.zero();
|
||||||
if (enemy.stunned == 0) {
|
if (enemy.stunned == 0) {
|
||||||
const directionToPlayer = toPlayer.normalize();
|
const directionToPlayer = toPlayer.normalize();
|
||||||
enemy.velocity = enemy.velocity.add(directionToPlayer.scale(enemy.acceleration * dt));
|
acceleration = acceleration.add(directionToPlayer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var enemyPushForce = rl.Vector2.zero();
|
||||||
|
for (enemies.items) |*otherEnemy| {
|
||||||
|
if (otherEnemy == enemy) continue;
|
||||||
|
|
||||||
|
const difference = enemy.position.sub(otherEnemy.position);
|
||||||
|
const enemyDistance = difference.length();
|
||||||
|
if (otherEnemy.size + enemy.size > enemyDistance*1.2) {
|
||||||
|
enemyPushForce = enemyPushForce.add(difference.normalize());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
acceleration = acceleration.add(enemyPushForce);
|
||||||
|
|
||||||
|
enemy.velocity = enemy.velocity.add(acceleration.scale(enemy.acceleration * dt));
|
||||||
enemy.velocity = rl.Vector2ClampValue(enemy.velocity, 0, enemy.maxSpeed);
|
enemy.velocity = rl.Vector2ClampValue(enemy.velocity, 0, enemy.maxSpeed);
|
||||||
enemy.velocity = rl.Vector2Scale(enemy.velocity, std.math.pow(f32, (1 - enemyFriction), dt));
|
enemy.velocity = rl.Vector2Scale(enemy.velocity, std.math.pow(f32, (1 - enemyFriction), dt));
|
||||||
enemy.position = enemy.position.add(enemy.velocity.scale(dt));
|
enemy.position = enemy.position.add(enemy.velocity.scale(dt));
|
||||||
@ -228,27 +319,15 @@ fn tickEnemies(self: *Self) !void {
|
|||||||
enemy.stunned = @max(0, enemy.stunned - dt);
|
enemy.stunned = @max(0, enemy.stunned - dt);
|
||||||
}
|
}
|
||||||
|
|
||||||
const deathBallPosition = rope.nodePositions.get(rope.nodePositions.len-1);
|
{ // Remove dead enemies
|
||||||
const deathBallVelocity = rope.nodeVelocities.get(rope.nodePositions.len-1);
|
|
||||||
|
|
||||||
{
|
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
while (i < enemies.items.len) {
|
while (i < enemies.items.len) {
|
||||||
const enemy = &enemies.items[i];
|
const enemy = &enemies.items[i];
|
||||||
|
|
||||||
if (enemy.invincibility == 0) {
|
if (enemy.health == 0) {
|
||||||
if (rl.Vector2Distance(enemy.position, deathBallPosition) < enemy.size + deathBallSize) {
|
self.killCount += 1;
|
||||||
enemy.health -= 1;
|
_ = enemies.swapRemove(i);
|
||||||
enemy.invincibility = 0.5;
|
continue;
|
||||||
enemy.stunned = 0.75;
|
|
||||||
enemy.velocity = deathBallVelocity.scale(20000);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (enemy.health == 0) {
|
|
||||||
self.killCount += 1;
|
|
||||||
_ = enemies.swapRemove(i);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
i += 1;
|
i += 1;
|
||||||
@ -256,55 +335,202 @@ fn tickEnemies(self: *Self) !void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn damagePlayer(self: *Self) void {
|
||||||
|
if (self.player.alive()) {
|
||||||
|
self.player.health -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!self.player.alive()) {
|
||||||
|
self.player.input.deactivate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn damageEnemy(self: *Self, enemy: *Enemy) void {
|
||||||
|
const deathBallVelocity = self.rope.lastNodeVelocity();
|
||||||
|
|
||||||
|
if (enemy.alive()) {
|
||||||
|
enemy.health -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
enemy.invincibility = 0.5;
|
||||||
|
enemy.stunned = 0.75;
|
||||||
|
enemy.velocity = deathBallVelocity.scale(20000);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn getScreenSize() rl.Vector2 {
|
||||||
|
return rl.Vector2{
|
||||||
|
.x = @floatFromInt(rl.GetScreenWidth()),
|
||||||
|
.y = @floatFromInt(rl.GetScreenHeight())
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn allocDrawText(
|
||||||
|
allocator: Allocator,
|
||||||
|
comptime fmt: []const u8,
|
||||||
|
fmt_args: anytype,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
font_size: i32,
|
||||||
|
color: rl.Color,
|
||||||
|
) !void {
|
||||||
|
const text = try std.fmt.allocPrintZ(allocator, fmt, fmt_args);
|
||||||
|
defer allocator.free(text);
|
||||||
|
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.begin();
|
||||||
|
defer self.ui.end();
|
||||||
|
|
||||||
|
var allocator = self.allocator;
|
||||||
|
const screen_box = UIBox.initScreen();
|
||||||
|
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);
|
||||||
|
|
||||||
|
const minutes_text = try std.fmt.allocPrint(allocator, "{d:.0}", .{self.timePassed/60});
|
||||||
|
defer allocator.free(minutes_text);
|
||||||
|
|
||||||
|
const seconds_text = try std.fmt.allocPrint(allocator, "{d:.3}", .{@mod(self.timePassed, 60)});
|
||||||
|
defer allocator.free(seconds_text);
|
||||||
|
|
||||||
|
const time_passed_text = try std.mem.concatWithSentinel(allocator, u8, &.{ minutes_text, ":", seconds_text }, 0);
|
||||||
|
defer allocator.free(time_passed_text);
|
||||||
|
|
||||||
|
const minutes_text_z = try std.mem.concatWithSentinel(allocator, u8, &.{ minutes_text }, 0);
|
||||||
|
defer allocator.free(minutes_text_z);
|
||||||
|
|
||||||
|
const font_size = 48;
|
||||||
|
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(
|
||||||
|
font,
|
||||||
|
time_passed_text,
|
||||||
|
time_passed_pos,
|
||||||
|
font_size,
|
||||||
|
4.8,
|
||||||
|
rl.GREEN
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!self.player.alive()) {
|
||||||
|
const modal_size = rl.Vector2{ .x = 200, .y = 400 };
|
||||||
|
const modal = screen_box.box(
|
||||||
|
screen_box.center() - modal_size.x/2,
|
||||||
|
screen_box.middle() - modal_size.y/2,
|
||||||
|
modal_size.x,
|
||||||
|
modal_size.y
|
||||||
|
);
|
||||||
|
const content = modal.margin(10);
|
||||||
|
|
||||||
|
rl.DrawRectangleRec(modal.rect(), rl.RAYWHITE);
|
||||||
|
UI.drawTextAligned(
|
||||||
|
font,
|
||||||
|
"You died!",
|
||||||
|
content.center_top().add(.{ .x = 0, .y = 30 }),
|
||||||
|
30,
|
||||||
|
rl.BLACK
|
||||||
|
);
|
||||||
|
|
||||||
|
if (self.ui.button("Restart?", content.left(), content.top() + 70, content.width, 30)) {
|
||||||
|
self.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self.ui.button("Exit?", content.left(), content.top() + 120, content.width, 30)) {
|
||||||
|
self.should_close = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn tick(self: *Self) !void {
|
pub fn tick(self: *Self) !void {
|
||||||
rl.ClearBackground(rl.BLACK);
|
rl.ClearBackground(rl.BLACK);
|
||||||
|
|
||||||
const screenWidth: f32 = @floatFromInt(rl.GetScreenWidth());
|
const screenSize = getScreenSize();
|
||||||
const screenHeight: f32 = @floatFromInt(rl.GetScreenHeight());
|
|
||||||
|
|
||||||
self.camera.offset.x = screenWidth/2;
|
self.camera.offset.x = screenSize.x/2;
|
||||||
self.camera.offset.y = screenHeight/2;
|
self.camera.offset.y = screenSize.y/2;
|
||||||
self.camera.target = self.player.position;
|
self.camera.target = self.player.position;
|
||||||
|
|
||||||
const dt = rl.GetFrameTime();
|
|
||||||
var player = &self.player;
|
var player = &self.player;
|
||||||
var enemies = &self.enemies;
|
var enemies = &self.enemies;
|
||||||
var rope = &self.rope;
|
var rope = &self.rope;
|
||||||
var allocator = self.allocator;
|
|
||||||
|
|
||||||
player.handDirection = PlayerInput.getHandPosition();
|
|
||||||
|
|
||||||
const moveDir = PlayerInput.getWalkDirection();
|
|
||||||
player.acceleration = moveDir;
|
|
||||||
player.acceleration = rl.Vector2Scale(player.acceleration, walkForce);
|
|
||||||
|
|
||||||
player.velocity = rl.Vector2Add(player.velocity, rl.Vector2Scale(player.acceleration, dt));
|
|
||||||
player.velocity = rl.Vector2ClampValue(player.velocity, 0, maxSpeed);
|
|
||||||
player.velocity = rl.Vector2Scale(player.velocity, std.math.pow(f32, (1 - friction), dt));
|
|
||||||
|
|
||||||
player.position = rl.Vector2Add(player.position, rl.Vector2Scale(player.velocity, dt));
|
|
||||||
|
|
||||||
const handPosition = player.position.add(player.handDirection.scale(handDistance));
|
|
||||||
|
|
||||||
const rope_root_node: rl.Vector2 = rope.nodePositions.get(0);
|
|
||||||
rope.nodePositions.set(0, rope_root_node.lerp(handPosition, 0.25));
|
|
||||||
|
|
||||||
rope.update(dt);
|
|
||||||
|
|
||||||
const deathBallPosition = rope.nodePositions.get(rope.nodePositions.len-1);
|
|
||||||
|
|
||||||
|
self.tickPlayer();
|
||||||
try self.tickEnemies();
|
try self.tickEnemies();
|
||||||
|
|
||||||
{ // UI
|
if (self.player.alive()) {
|
||||||
const enemyCountText = try std.fmt.allocPrintZ(allocator, "{d}", .{self.spawnedEnemiesCount});
|
self.timePassed += rl.GetFrameTime();
|
||||||
defer allocator.free(enemyCountText);
|
|
||||||
rl.DrawText(enemyCountText, 10, 10, 24, rl.RED);
|
|
||||||
|
|
||||||
const killCountText = try std.fmt.allocPrintZ(allocator, "{d}", .{self.killCount});
|
|
||||||
defer allocator.free(killCountText);
|
|
||||||
rl.DrawText(killCountText, 10, 30, 24, rl.GREEN);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const deathBallPosition = rope.nodePositions.get(rope.nodePositions.len-1);
|
||||||
|
const handPosition = player.getHandPosition();
|
||||||
|
|
||||||
rl.BeginMode2D(self.camera);
|
rl.BeginMode2D(self.camera);
|
||||||
{
|
{
|
||||||
rl.DrawCircle(0, 0, 5, rl.GOLD);
|
rl.DrawCircle(0, 0, 5, rl.GOLD);
|
||||||
@ -329,13 +555,6 @@ pub fn tick(self: *Self) !void {
|
|||||||
rl.GREEN
|
rl.GREEN
|
||||||
);
|
);
|
||||||
|
|
||||||
rl.DrawCircle(
|
|
||||||
@intFromFloat(player.position.x + moveDir.x * maxSpeed),
|
|
||||||
@intFromFloat(player.position.y + moveDir.y * maxSpeed),
|
|
||||||
5,
|
|
||||||
rl.GREEN
|
|
||||||
);
|
|
||||||
|
|
||||||
for (enemies.items) |*enemy| {
|
for (enemies.items) |*enemy| {
|
||||||
var color = enemy.color;
|
var color = enemy.color;
|
||||||
if (enemy.invincibility > 0 and @rem(enemy.invincibility, 0.2) < 0.1) {
|
if (enemy.invincibility > 0 and @rem(enemy.invincibility, 0.2) < 0.1) {
|
||||||
@ -380,4 +599,6 @@ pub fn tick(self: *Self) !void {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
rl.EndMode2D();
|
rl.EndMode2D();
|
||||||
|
|
||||||
|
try self.tickUI();
|
||||||
}
|
}
|
||||||
|
@ -10,13 +10,13 @@ pub fn main() !void {
|
|||||||
|
|
||||||
rl.SetTargetFPS(60);
|
rl.SetTargetFPS(60);
|
||||||
rl.InitWindow(1200, 1200, "Step kill");
|
rl.InitWindow(1200, 1200, "Step kill");
|
||||||
rl.DisableCursor();
|
rl.SetExitKey(.KEY_NULL);
|
||||||
defer rl.CloseWindow();
|
defer rl.CloseWindow();
|
||||||
|
|
||||||
var scene = MainScene.init(allocator);
|
var scene = MainScene.init(allocator);
|
||||||
defer scene.deinit();
|
defer scene.deinit();
|
||||||
|
|
||||||
while (!rl.WindowShouldClose()) {
|
while (!rl.WindowShouldClose() and !scene.should_close) {
|
||||||
rl.BeginDrawing();
|
rl.BeginDrawing();
|
||||||
defer rl.EndDrawing();
|
defer rl.EndDrawing();
|
||||||
|
|
||||||
|
@ -1,11 +1,18 @@
|
|||||||
const rl = @import("raylib");
|
const rl = @import("raylib");
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
const gamepad: i32 = 0;
|
gamepad: i32 = 0,
|
||||||
|
active: bool = false,
|
||||||
|
|
||||||
|
last_hand_position: rl.Vector2 = rl.Vector2.zero(),
|
||||||
|
mouse_hand_position: rl.Vector2 = rl.Vector2.zero(),
|
||||||
|
|
||||||
var mouseHandPosition: rl.Vector2 = .{ .x = 0, .y = 0 };
|
|
||||||
const mouseHandRadius = 100.0;
|
const mouseHandRadius = 100.0;
|
||||||
|
|
||||||
|
pub fn init() @This() {
|
||||||
|
return @This(){};
|
||||||
|
}
|
||||||
|
|
||||||
fn clampVector(vec: rl.Vector2) rl.Vector2 {
|
fn clampVector(vec: rl.Vector2) rl.Vector2 {
|
||||||
if (vec.length2() > 1) {
|
if (vec.length2() > 1) {
|
||||||
return vec.normalize();
|
return vec.normalize();
|
||||||
@ -30,10 +37,10 @@ fn getKeyboardWalkDirection() rl.Vector2 {
|
|||||||
dx += 1;
|
dx += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return rl.Vector2{ .x = dx, .y = dy };
|
return (rl.Vector2{ .x = dx, .y = dy }).normalize();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn getGamepadWalkDirection() ?rl.Vector2 {
|
fn getGamepadWalkDirection(gamepad: i32) ?rl.Vector2 {
|
||||||
if (!rl.IsGamepadAvailable(gamepad)) {
|
if (!rl.IsGamepadAvailable(gamepad)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -48,16 +55,20 @@ fn getGamepadWalkDirection() ?rl.Vector2 {
|
|||||||
return clampVector(.{ .x = x, .y = y });
|
return clampVector(.{ .x = x, .y = y });
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getWalkDirection() rl.Vector2 {
|
pub fn getWalkDirection(self: *@This()) rl.Vector2 {
|
||||||
var walkDirection = getKeyboardWalkDirection().normalize();
|
if (!self.active) {
|
||||||
if (getGamepadWalkDirection()) |dir| {
|
return rl.Vector2.zero();
|
||||||
walkDirection = dir;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return walkDirection;
|
var walk_direction = getKeyboardWalkDirection();
|
||||||
|
if (getGamepadWalkDirection(self.gamepad)) |dir| {
|
||||||
|
walk_direction = dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
return walk_direction;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn getGamepadHandPosition() ?rl.Vector2 {
|
fn getGamepadHandPosition(gamepad: i32) ?rl.Vector2 {
|
||||||
if (!rl.IsGamepadAvailable(gamepad)) {
|
if (!rl.IsGamepadAvailable(gamepad)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -72,19 +83,35 @@ fn getGamepadHandPosition() ?rl.Vector2 {
|
|||||||
return clampVector(.{ .x = x, .y = y });
|
return clampVector(.{ .x = x, .y = y });
|
||||||
}
|
}
|
||||||
|
|
||||||
fn getMouseHandPosition() rl.Vector2 {
|
fn getMouseHandPosition(self: *@This()) rl.Vector2 {
|
||||||
const mouseDelta = rl.GetMouseDelta();
|
const mouse_delta = rl.GetMouseDelta();
|
||||||
|
|
||||||
mouseHandPosition = clampVector(mouseHandPosition.add(mouseDelta.scale(1.0/mouseHandRadius)));
|
self.mouse_hand_position = clampVector(self.mouse_hand_position.add(mouse_delta.scale(1.0/mouseHandRadius)));
|
||||||
|
return self.mouse_hand_position;
|
||||||
return mouseHandPosition;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getHandPosition() rl.Vector2 {
|
pub fn getHandPosition(self: *@This()) rl.Vector2 {
|
||||||
var handPosition = getMouseHandPosition();
|
if (!self.active) {
|
||||||
if (getGamepadHandPosition()) |pos| {
|
return self.last_hand_position;
|
||||||
handPosition = pos;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return clampVector(handPosition);
|
var hand_position = self.getMouseHandPosition();
|
||||||
|
if (getGamepadHandPosition(self.gamepad)) |pos| {
|
||||||
|
hand_position = pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.last_hand_position = hand_position;
|
||||||
|
return hand_position;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn activate(self: *@This()) void {
|
||||||
|
if (self.active) return;
|
||||||
|
rl.DisableCursor();
|
||||||
|
self.active = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deactivate(self: *@This()) void {
|
||||||
|
if (!self.active) return;
|
||||||
|
rl.EnableCursor();
|
||||||
|
self.active = false;
|
||||||
}
|
}
|
||||||
|
121
src/ui.zig
Normal file
121
src/ui.zig
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
const rl = @import("raylib");
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const gamepad: i32 = 0;
|
||||||
|
|
||||||
|
selected_button: i32 = -1,
|
||||||
|
last_button_idx: u32 = 0,
|
||||||
|
button_count: u32 = 0,
|
||||||
|
|
||||||
|
was_mouse_hot: bool = false,
|
||||||
|
is_down: bool = false,
|
||||||
|
is_released: bool = false,
|
||||||
|
first_render: bool = true,
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn button(self: *@This(), text: [:0]const u8, x: f32, y: f32, width: f32, height: f32) bool {
|
||||||
|
const button_idx = self.last_button_idx;
|
||||||
|
self.last_button_idx += 1;
|
||||||
|
self.button_count += 1;
|
||||||
|
|
||||||
|
if (didMouseMove() and isMouseInBounds(x, y, width, height)) {
|
||||||
|
self.selected_button = @intCast(button_idx);
|
||||||
|
self.was_mouse_hot = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var is_hot = self.selected_button == @as(i32, @intCast(button_idx));
|
||||||
|
|
||||||
|
const rect = rl.Rectangle{
|
||||||
|
.x = x,
|
||||||
|
.y = y,
|
||||||
|
.width = width,
|
||||||
|
.height = height,
|
||||||
|
};
|
||||||
|
if (is_hot and self.is_down) {
|
||||||
|
rl.DrawRectangleRec(rect, rl.RED);
|
||||||
|
} else if (is_hot) {
|
||||||
|
rl.DrawRectangleRec(rect, rl.ORANGE);
|
||||||
|
} else {
|
||||||
|
rl.DrawRectangleRec(rect, rl.GREEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
drawTextAligned(rl.GetFontDefault(), text, .{ .x = x + width/2, .y = y + height/2 }, height * 0.8, rl.BLACK);
|
||||||
|
|
||||||
|
return is_hot and self.is_released;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn begin(self: *@This()) void {
|
||||||
|
if (self.first_render and self.button_count > 0) {
|
||||||
|
if (rl.IsGamepadAvailable(gamepad)) {
|
||||||
|
self.selected_button = 0;
|
||||||
|
}
|
||||||
|
self.first_render = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.last_button_idx = 0;
|
||||||
|
|
||||||
|
self.was_mouse_hot = false;
|
||||||
|
self.is_down = false;
|
||||||
|
self.is_released = false;
|
||||||
|
|
||||||
|
if (rl.IsGamepadAvailable(gamepad)) {
|
||||||
|
const btn = rl.GamepadButton.GAMEPAD_BUTTON_RIGHT_FACE_DOWN;
|
||||||
|
self.is_down = self.is_down or rl.IsGamepadButtonDown(gamepad, btn);
|
||||||
|
self.is_released = self.is_released or rl.IsGamepadButtonReleased(gamepad, btn);
|
||||||
|
|
||||||
|
if (self.button_count > 0) {
|
||||||
|
const pressed_down = rl.IsGamepadButtonPressed(gamepad, .GAMEPAD_BUTTON_LEFT_FACE_DOWN);
|
||||||
|
const pressed_up = rl.IsGamepadButtonPressed(gamepad, .GAMEPAD_BUTTON_LEFT_FACE_UP);
|
||||||
|
if ((pressed_up or pressed_down) and self.selected_button == -1) {
|
||||||
|
self.selected_button = 0;
|
||||||
|
} else {
|
||||||
|
if (pressed_down) {
|
||||||
|
self.selected_button = @mod(self.selected_button + 1, @as(i32, @intCast(self.button_count)));
|
||||||
|
}
|
||||||
|
if (pressed_up) {
|
||||||
|
self.selected_button = @mod(self.selected_button - 1, @as(i32, @intCast(self.button_count)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rl.IsCursorHidden()) {
|
||||||
|
const btn = rl.MouseButton.MOUSE_BUTTON_LEFT;
|
||||||
|
self.is_down = self.is_down or rl.IsMouseButtonDown(btn);
|
||||||
|
self.is_released = self.is_released or rl.IsMouseButtonReleased(btn);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.button_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn end(self: *@This()) void {
|
||||||
|
if (!self.was_mouse_hot and didMouseMove()) {
|
||||||
|
self.selected_button = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn drawTextAligned(
|
||||||
|
font: rl.Font,
|
||||||
|
text: [*:0]const u8,
|
||||||
|
position: rl.Vector2,
|
||||||
|
font_size: f32,
|
||||||
|
tint: rl.Color,
|
||||||
|
) void {
|
||||||
|
const default_font_size: f32 = 10;
|
||||||
|
const spacing: f32 = @max(font_size/default_font_size, 1);
|
||||||
|
const text_size = rl.MeasureTextEx(font, text, font_size, spacing);
|
||||||
|
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