create mace prototype
This commit is contained in:
parent
cd334f1a84
commit
be6be4390b
301
src/main-scene.zig
Normal file
301
src/main-scene.zig
Normal file
@ -0,0 +1,301 @@
|
||||
const rl = @import("raylib");
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const assert = std.debug.assert;
|
||||
|
||||
const friction = 0.99;
|
||||
const walkForce = 4000;
|
||||
const maxSpeed = 200;
|
||||
const handDistance = 50;
|
||||
|
||||
const Enemy = struct {
|
||||
position: rl.Vector2,
|
||||
};
|
||||
|
||||
const Player = struct {
|
||||
position: rl.Vector2,
|
||||
velocity: rl.Vector2 = rl.Vector2.zero(),
|
||||
acceleration: rl.Vector2 = rl.Vector2.zero(),
|
||||
handDirection: rl.Vector2 = rl.Vector2{ .x = 1, .y = 0 }
|
||||
};
|
||||
|
||||
const Rope = struct {
|
||||
const maxNodes = 64;
|
||||
const NodeArray = std.BoundedArray(rl.Vector2, maxNodes);
|
||||
|
||||
nodePositions: NodeArray,
|
||||
nodeVelocities: NodeArray,
|
||||
segmentLength: f32,
|
||||
springForce: f32 = 10,
|
||||
|
||||
leftoverTime: f32 = 0,
|
||||
friction: f32 = 0.0005,
|
||||
|
||||
fn init(from: rl.Vector2, to: rl.Vector2, nodeCount: u32) Rope {
|
||||
assert(nodeCount <= maxNodes);
|
||||
var nodePositions = NodeArray.init(nodeCount) catch unreachable;
|
||||
|
||||
const diff = to.sub(from);
|
||||
const segmentLength = diff.length() / @as(f32, @floatFromInt(nodeCount));
|
||||
const segmentStep = diff.normalize().scale(segmentLength);
|
||||
|
||||
nodePositions.set(0, from);
|
||||
for (1..nodeCount) |i| {
|
||||
const nextNode = nodePositions.get(i-1).add(segmentStep);
|
||||
nodePositions.set(i, nextNode);
|
||||
}
|
||||
|
||||
var nodeVelocities = NodeArray.init(nodeCount) catch unreachable;
|
||||
for (nodeVelocities.slice()) |*vel| {
|
||||
vel.* = rl.Vector2.zero();
|
||||
}
|
||||
|
||||
return Rope{
|
||||
.nodePositions = nodePositions,
|
||||
.nodeVelocities = nodeVelocities,
|
||||
.segmentLength = segmentLength
|
||||
};
|
||||
}
|
||||
|
||||
fn update(self: *Rope, dt: f32) void {
|
||||
const timestep = 1.0/(60.0 * 100);
|
||||
self.leftoverTime += dt;
|
||||
while (self.leftoverTime >= timestep) : (self.leftoverTime -= timestep) {
|
||||
self.update_step(timestep);
|
||||
}
|
||||
}
|
||||
|
||||
fn update_step(self: *Rope, dt: f32) void {
|
||||
var offsets = NodeArray.init(self.nodePositions.len) catch unreachable;
|
||||
for (offsets.slice()) |*offset| {
|
||||
offset.* = rl.Vector2.zero();
|
||||
}
|
||||
|
||||
var accelerations = NodeArray.init(self.nodePositions.len) catch unreachable;
|
||||
for (accelerations.slice()) |*acc| {
|
||||
acc.* = rl.Vector2.zero();
|
||||
}
|
||||
|
||||
const node_count = self.nodePositions.len;
|
||||
for (1..node_count) |i| {
|
||||
const node1: rl.Vector2 = self.nodePositions.get(i-1);
|
||||
const node2: rl.Vector2 = self.nodePositions.get(i);
|
||||
const node_diff = node2.sub(node1);
|
||||
const node_dir = node_diff.normalize();
|
||||
|
||||
const rope_force = node_diff.length() - self.segmentLength;
|
||||
const d = node_dir.scale(rope_force);
|
||||
|
||||
offsets.set(i-1, offsets.get(i-1).add(d.scale(0.5)));
|
||||
offsets.set(i , offsets.get(i ).add(d.scale(-0.5)));
|
||||
|
||||
accelerations.set(i-1, accelerations.get(i-1).add(d));
|
||||
accelerations.set(i , accelerations.get(i ).add(d.scale(-1)));
|
||||
}
|
||||
|
||||
for (1..node_count) |i| {
|
||||
const pos = &self.nodePositions.slice()[i];
|
||||
const vel = &self.nodeVelocities.slice()[i];
|
||||
const acc = accelerations.get(i);
|
||||
const offset = offsets.get(i);
|
||||
vel.* = vel.add(acc.scale(dt));
|
||||
vel.* = vel.scale(1 - self.friction);
|
||||
pos.* = pos.add(vel.scale(dt*10000));
|
||||
pos.* = pos.add(offset.scale(0.01));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
allocator: Allocator,
|
||||
prng: std.rand.DefaultPrng,
|
||||
|
||||
enemyTimer: f32 = 0,
|
||||
spawnedEnemiesCount: u32 = 0,
|
||||
killCount: u32 = 0,
|
||||
enemies: std.ArrayList(Enemy),
|
||||
player: Player,
|
||||
rope: Rope,
|
||||
|
||||
const Self = @This();
|
||||
|
||||
pub fn init(allocator: Allocator) Self {
|
||||
var playerPosition = rl.Vector2{ .x = 400, .y = 400 };
|
||||
var ropeSize: f32 = 200;
|
||||
var handPosition = playerPosition.add(rl.Vector2{ .x = handDistance, .y = 0});
|
||||
|
||||
return Self {
|
||||
.allocator = allocator,
|
||||
.prng = std.rand.DefaultPrng.init(@bitCast(std.time.timestamp())),
|
||||
.enemies = std.ArrayList(Enemy).init(allocator),
|
||||
.player = .{ .position = playerPosition },
|
||||
.rope = Rope.init(handPosition, handPosition.add(.{ .x = ropeSize, .y = 0.002 }), 10)
|
||||
};
|
||||
}
|
||||
|
||||
pub fn reset(self: *Self) void {
|
||||
self.deinit();
|
||||
self.* = Self.init(self.allocator);
|
||||
}
|
||||
|
||||
pub fn deinit(self: Self) void {
|
||||
self.enemies.deinit();
|
||||
}
|
||||
|
||||
pub fn tick(self: *Self) !void {
|
||||
rl.ClearBackground(rl.BLACK);
|
||||
|
||||
const dt = rl.GetFrameTime();
|
||||
var rng = self.prng.random();
|
||||
_ = rng;
|
||||
var player = &self.player;
|
||||
var enemies = &self.enemies;
|
||||
var rope = &self.rope;
|
||||
var allocator = self.allocator;
|
||||
|
||||
self.enemyTimer -= dt;
|
||||
// if (self.enemyTimer <= 0) {
|
||||
// self.enemyTimer = 0.5 + rng.float(f32) * 2;
|
||||
// self.spawnedEnemiesCount += 1;
|
||||
// try enemies.append(Enemy{
|
||||
// .position = rl.Vector2.randomOnUnitCircle(rng).scale(100)
|
||||
// });
|
||||
// }
|
||||
|
||||
var inputDx: f32 = 0;
|
||||
var inputDy: f32 = 0;
|
||||
if (rl.IsKeyDown(rl.KeyboardKey.KEY_W)) {
|
||||
inputDy -= 1;
|
||||
}
|
||||
if (rl.IsKeyDown(rl.KeyboardKey.KEY_S)) {
|
||||
inputDy += 1;
|
||||
}
|
||||
if (rl.IsKeyDown(rl.KeyboardKey.KEY_A)) {
|
||||
inputDx -= 1;
|
||||
}
|
||||
if (rl.IsKeyDown(rl.KeyboardKey.KEY_D)) {
|
||||
inputDx += 1;
|
||||
}
|
||||
|
||||
var input_dir = rl.Vector2{ .x = inputDx, .y = inputDy };
|
||||
input_dir = rl.Vector2Normalize(input_dir);
|
||||
if (input_dir.x != 0 or input_dir.y != 0) {
|
||||
player.handDirection = input_dir;
|
||||
}
|
||||
|
||||
player.acceleration = input_dir;
|
||||
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 enemySize: f32 = 20;
|
||||
const deathBallSize: f32 = 10;
|
||||
const playerSize = 20;
|
||||
|
||||
const enemySpeed = 100;
|
||||
for (enemies.items) |*enemy| {
|
||||
const toPlayer = player.position.sub(enemy.position);
|
||||
if (toPlayer.length() <= playerSize + enemySize) {
|
||||
self.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
const directionToPlayer = toPlayer.normalize();
|
||||
enemy.position = enemy.position.add(directionToPlayer.scale(enemySpeed * dt));
|
||||
rl.DrawCircle(
|
||||
@intFromFloat(enemy.position.x),
|
||||
@intFromFloat(enemy.position.y),
|
||||
enemySize,
|
||||
rl.RED
|
||||
);
|
||||
}
|
||||
|
||||
const handPosition = player.position.add(player.handDirection.scale(handDistance));
|
||||
|
||||
const enemyCountText = try std.fmt.allocPrintZ(allocator, "{d}", .{self.spawnedEnemiesCount});
|
||||
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);
|
||||
|
||||
rl.DrawCircle(
|
||||
@intFromFloat(player.position.x),
|
||||
@intFromFloat(player.position.y),
|
||||
playerSize,
|
||||
rl.RAYWHITE
|
||||
);
|
||||
rl.DrawCircle(
|
||||
@intFromFloat(handPosition.x),
|
||||
@intFromFloat(handPosition.y),
|
||||
5,
|
||||
rl.RAYWHITE
|
||||
);
|
||||
rl.DrawLine(
|
||||
@intFromFloat(player.position.x),
|
||||
@intFromFloat(player.position.y),
|
||||
@intFromFloat(player.position.x + player.velocity.x),
|
||||
@intFromFloat(player.position.y + player.velocity.y),
|
||||
rl.GREEN
|
||||
);
|
||||
|
||||
rl.DrawCircle(
|
||||
@intFromFloat(player.position.x + input_dir.x * maxSpeed),
|
||||
@intFromFloat(player.position.y + input_dir.y * maxSpeed),
|
||||
5,
|
||||
rl.GREEN
|
||||
);
|
||||
|
||||
rope.nodePositions.set(0, handPosition);
|
||||
|
||||
rope.update(dt);
|
||||
|
||||
const rope_color = rl.PURPLE;
|
||||
for (0..(rope.nodePositions.len-1)) |i| {
|
||||
var node1: rl.Vector2 = rope.nodePositions.get(i);
|
||||
var node2: rl.Vector2 = rope.nodePositions.get(i+1);
|
||||
rl.DrawLine(
|
||||
@intFromFloat(node1.x),
|
||||
@intFromFloat(node1.y),
|
||||
@intFromFloat(node2.x),
|
||||
@intFromFloat(node2.y),
|
||||
rope_color
|
||||
);
|
||||
}
|
||||
|
||||
for (rope.nodePositions.slice()) |node| {
|
||||
rl.DrawCircle(
|
||||
@intFromFloat(node.x),
|
||||
@intFromFloat(node.y),
|
||||
5,
|
||||
rope_color
|
||||
);
|
||||
}
|
||||
|
||||
const deathBallPosition = rope.nodePositions.get(rope.nodePositions.len-1);
|
||||
|
||||
rl.DrawCircle(
|
||||
@intFromFloat(deathBallPosition.x),
|
||||
@intFromFloat(deathBallPosition.y),
|
||||
deathBallSize,
|
||||
rl.RED
|
||||
);
|
||||
|
||||
{
|
||||
var i: usize = 0;
|
||||
while (i < enemies.items.len) {
|
||||
const enemy = &enemies.items[i];
|
||||
const distanceToDeathBall = enemy.position.sub(deathBallPosition).length();
|
||||
if (distanceToDeathBall < enemySize + deathBallSize) {
|
||||
self.killCount += 1;
|
||||
_ = enemies.swapRemove(i);
|
||||
continue;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
21
src/main.zig
21
src/main.zig
@ -1,19 +1,24 @@
|
||||
const rl = @import("raylib");
|
||||
const std = @import("std");
|
||||
|
||||
const MainScene = @import("main-scene.zig");
|
||||
|
||||
pub fn main() !void {
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
var allocator = gpa.allocator();
|
||||
defer _ = gpa.deinit();
|
||||
|
||||
pub fn main() void {
|
||||
rl.SetConfigFlags(rl.ConfigFlags{ .FLAG_WINDOW_RESIZABLE = true });
|
||||
rl.InitWindow(800, 800, "hello world!");
|
||||
rl.SetTargetFPS(60);
|
||||
|
||||
rl.InitWindow(800, 800, "Step kill");
|
||||
defer rl.CloseWindow();
|
||||
|
||||
var scene = MainScene.init(allocator);
|
||||
defer scene.deinit();
|
||||
|
||||
while (!rl.WindowShouldClose()) {
|
||||
rl.BeginDrawing();
|
||||
defer rl.EndDrawing();
|
||||
|
||||
rl.ClearBackground(rl.BLACK);
|
||||
rl.DrawFPS(10, 10);
|
||||
|
||||
rl.DrawText("hello world!", 100, 100, 20, rl.YELLOW);
|
||||
try scene.tick();
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user