~alva/bee

e840451b21d5be9880c509db8b7a7cf4091e15ad — Alva 4 years ago
Initial commit
A  => LICENSE +19 -0
@@ 1,19 @@
Copyright (c) 2019 Alva

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

A  => README.md +17 -0
@@ 1,17 @@
# Bee

A bee-based shoot 'em up.

## Credits

- Pixel art: [Leo](https://beach.city/@crowlad)
- Programming: [Alva](https://beach.city/@crowlad)

## Building and running

Using Zig 0.6.0:

```shell
# build it [optimised] [and run it]
zig build [-Drelease-fast] [run]
```

A  => assets/bee-sheet.png +0 -0
A  => assets/bee16x16.gif +0 -0
A  => assets/bee16x16.png +0 -0
A  => assets/bee16x16goingup.png +0 -0
A  => assets/beeshootums32x16.png +0 -0
A  => assets/flower-sheet.png +0 -0
A  => build.zig +28 -0
@@ 1,28 @@
const Builder = @import("std").build.Builder;
const name = "Bee";

pub fn build(b: *Builder) void {
    const mode = b.standardReleaseOptions();
    const exe = b.addExecutable("bee", "src/main.zig");

    exe.setBuildMode(mode);

    if (mode != .Debug)
        exe.strip = true;

    if (mode != .ReleaseSafe)
        exe.force_pic = true;

    exe.linkLibC();
    exe.linkSystemLibrary("vulkan");
    exe.linkSystemLibrary("SDL2");
    exe.linkSystemLibrary("SDL2_image");

    b.default_step.dependOn(&exe.step);
    b.installArtifact(exe);

    const run_cmd = exe.run();
    const run_step = b.step("run", "Run " ++ name);

    run_step.dependOn(&run_cmd.step);
}

A  => src/assets.zig +2 -0
@@ 1,2 @@
pub const bee = @embedFile("../assets/bee-sheet.png");
pub const flower = @embedFile("../assets/flower-sheet.png");

A  => src/c.zig +4 -0
@@ 1,4 @@
usingnamespace @cImport({
    @cInclude("SDL2/SDL.h");
    @cInclude("SDL2/SDL_image.h");
});

A  => src/main.zig +171 -0
@@ 1,171 @@
const c = @import("c.zig");
const std = @import("std");
const assets = @import("assets.zig");
usingnamespace @import("objects.zig");

const assert = std.debug.assert;
const warn = std.debug.warn;

const resolution = Vec2{256.0, 224.0};
const scale = 3.0;
const fps = 60.0;

pub fn main() !void {
    try initSdl();
    defer c.SDL_Quit();

    const win = try createSdlWindow();
    defer c.SDL_DestroyWindow(win);

    const renderer = try createSdlRenderer(win);
    defer c.SDL_DestroyRenderer(renderer);

    try configureSdlRenderer(renderer);

//    _ = c.SDL_SetWindowFullscreen(win, c.SDL_WINDOW_FULLSCREEN);

    stage = try Stage.init();
    defer stage.deinit();

    stage.textures[0] = try createSdlTexture(renderer, assets.bee);
    stage.textures[1] = try createSdlTexture(renderer, assets.flower);

    stage.player = Player{
        .mo = 0,
    };
//    _ = c.SDL_ShowCursor(0);
    stage.attributes[0] = Attributes{
        .enabled = true,
        .position = .{0.0, resolution[1] / 2 - 8},
        .speed = .{0.0, 0.0},
        .direction = 0,
        .texture = 0,
        .whatever = undefined,
    };

    stage.attributes[1] = Attributes{
        .enabled = true,
        .position = .{resolution[1] / 2 + 8, resolution[1] - 16},
        .speed = .{0.0, 0.0},
        .direction = 0,
        .texture = 1,
        .whatever = undefined,
    };

    stage.attributes[2] = Attributes{
        .enabled = true,
        .position = .{resolution[1] / 2 + 32, resolution[1] - 16},
        .speed = .{0.0, 0.0},
        .direction = 0,
        .texture = 1,
        .whatever = undefined,
    };

    const pf = @intToFloat(f64, c.SDL_GetPerformanceFrequency());

    stage.tick_now = @intToFloat(f64, c.SDL_GetPerformanceCounter());

//    var dmode: c.SDL_DisplayMode = undefined;
//    if (c.SDL_GetCurrentDisplayMode(0, &dmode) != 0)
//        return error.SdlInitFailed;
//    const refresh = @intToFloat(f64, dmode.refresh_rate);
//    warn("{}\n", .{dmode.refresh_rate});

    _ = c.SDL_SetRenderDrawColor(renderer, 0, 0, 0, c.SDL_ALPHA_OPAQUE);
    _ = c.SDL_RenderClear(renderer);
    _ = c.SDL_SetRenderDrawColor(renderer, 32, 64, 128, c.SDL_ALPHA_OPAQUE);
    _ = c.SDL_RenderFillRect(renderer, null);

    var event: c.SDL_Event = undefined;

    loop: while (true) {
        stage.tick_now = @intToFloat(f64, c.SDL_GetPerformanceCounter());
        const delta = (stage.tick_now - stage.tick_last) / pf;

        if (delta < 1.0 / fps) {
//            std.time.sleep(@floatToInt(u64, (1.0 / refresh - delta) * 1000000000.0));
            continue;
        }

        while (c.SDL_PollEvent(&event) != 0) {
            switch (event.type) {
                c.SDL_QUIT => {
                    break :loop;
                },
                c.SDL_KEYUP => {
                    stage.player.handleKeyUp(event.key.keysym.sym);
                },
                c.SDL_KEYDOWN => {
                    stage.player.handleKeyDown(event.key.keysym.sym);
                },
                else => {
                },
            }
        }

        stage.tick_last = stage.tick_now;

        _ = c.SDL_RenderFillRect(renderer, null);
        stage.update(delta);
        stage.render(renderer);
        c.SDL_RenderPresent(renderer);
    }
}

fn initSdl() !void {
    if (c.SDL_Init(c.SDL_INIT_VIDEO) != 0) {
//        warn("Unable to initialize SDL: {s}", .{c.SDL_GetError()});
        return error.SdlInitFailed;
    }
}

fn createSdlWindow() !*c.SDL_Window {
    return c.SDL_CreateWindow(
        "Bee",
        c.SDL_WINDOWPOS_CENTERED,
        c.SDL_WINDOWPOS_CENTERED,
        resolution[0] * scale,
        resolution[1] * scale,
        c.SDL_WINDOW_VULKAN | c.SDL_WINDOW_RESIZABLE,
    ) orelse {
//        warn("Failed to create window: {s}\n", .{c.SDL_GetError()});
        return error.SdlCreateWindowFailed;
    };
}

fn createSdlRenderer(window: *c.SDL_Window) !*c.SDL_Renderer {
    return c.SDL_CreateRenderer(window, -1, 0) orelse {
//        warn("Failed to create renderer: {s}\n", .{c.SDL_GetError()});
        return error.SdlCreateRendererFailed;
    };
}

fn configureSdlRenderer(renderer: *c.SDL_Renderer) !void {
    if (c.SDL_RenderSetLogicalSize(renderer, resolution[0], resolution[1]) != 0)
        return error.SdlRenderSetLogicalSizeFailed;

    if (c.SDL_RenderSetIntegerScale(renderer, .SDL_TRUE) != 0)
        return error.SdlRenderSetIntegerScaleFailed;
}

fn createSdlTexture(renderer: *c.SDL_Renderer, data: []const u8) !*c.SDL_Texture {
    const rw = c.SDL_RWFromConstMem(
        &data[0],
        @intCast(c_int, data.len),
    ) orelse {
//        warn("SDL_RWFromConstMem: {s}\n", .{c.SDL_GetError()});
        return error.SdlRwFromConstMemFailed;
    };
    defer assert(c.SDL_RWclose(rw) == 0);

    const surface = c.IMG_Load_RW(rw, 0) orelse {
//        warn("IMG_Load_RW: {s}\n", .{c.SDL_GetError()});
        return error.ImgLoadRwFailed;
    };
    defer c.SDL_FreeSurface(surface);

    return c.SDL_CreateTextureFromSurface(renderer, surface) orelse {
//        warn("SDL_CreateTextureFromSurface: {s}\n", .{c.SDL_GetError()});
        return error.SdlCreateTextureFromSurfaceFailed;
    };
}

A  => src/objects.zig +192 -0
@@ 1,192 @@
const std = @import("std");
const c = @import("c.zig");
const math = std.math;
const max = math.max;
const min = math.min;
const warn = std.debug.warn;

pub const Vec2 = @Vector(2, f64);
pub const Vec3 = @Vector(3, f64);
pub const Vec4 = @Vector(4, f64);

pub var stage: Stage = undefined;

pub const AxisAlignedBoundingBox = struct {
    center: Vec2,
    half_size: Vec2,
}; 

pub const Attributes = struct {
    enabled: bool,
    position: Vec2,
    speed: Vec2,
    direction: u8,
    texture: u8,
    whatever: u8,
};

const Direction = enum(u8) {
    None = 0,
    Up = 1 << 0,
    Down = 1 << 1,
    Left = 1 << 2,
    Right = 1 << 3,
};

pub const Player = struct {
    mo: u8,
    const Self = @This();
    const acc = 7.5;
    const dec = 2.5;
    const smax = 60.0;

    fn update(self: *Self, delta: f64) void {
        const mo = &stage.attributes[self.mo];

        if (mo.direction & @enumToInt(Direction.Up) != 0)
            mo.speed[1] = max(mo.speed[1] - acc, -smax);

        if (mo.direction & @enumToInt(Direction.Down) != 0)
            mo.speed[1] = min(mo.speed[1] + acc, smax);

        if (mo.direction & @enumToInt(Direction.Left) != 0)
            mo.speed[0] = max(mo.speed[0] - acc, -smax);

        if (mo.direction & @enumToInt(Direction.Right) != 0)
            mo.speed[0] = min(mo.speed[0] + acc, smax);

        if (mo.direction &
                (@enumToInt(Direction.Up) | @enumToInt(Direction.Down)) == 0) {
            if (mo.speed[1] < 0.0) {
                mo.speed[1] = min(mo.speed[1] + dec, 0.0);
            } else if (0.0 < mo.speed[1])
                mo.speed[1] = max(mo.speed[1] - dec, 0.0);
        }

        if (mo.direction &
                (@enumToInt(Direction.Right) | @enumToInt(Direction.Left)) == 0) {
            if (mo.speed[0] < 0.0) {
                mo.speed[0] = min(mo.speed[0] + dec, 0.0);
            } else if (0.0 < mo.speed[0])
                mo.speed[0] = max(mo.speed[0] - dec, 0.0);
        }

//        warn("Speed {}\n", .{mo.speed});
        mo.whatever +%= 32;
    }

    fn handleKeyUp(self: *Self, sym: c.SDL_Keycode) void {
        const a = &stage.attributes[self.mo];
        a.direction &= ~@enumToInt(switch (sym) {
            c.SDLK_UP, c.SDLK_w => Direction.Up,
            c.SDLK_DOWN, c.SDLK_s => Direction.Down,
            c.SDLK_LEFT, c.SDLK_a => Direction.Left,
            c.SDLK_RIGHT, c.SDLK_d => Direction.Right,
            else => Direction.None,
        });
    }

    fn handleKeyDown(self: *Self, sym: c.SDL_Keycode) void {
        if (sym == c.SDLK_SPACE) {
            warn("shootums\n", .{});
            return;
        }

        const a = &stage.attributes[self.mo];
        a.direction |= @enumToInt(switch (sym) {
            c.SDLK_UP, c.SDLK_w => Direction.Up,
            c.SDLK_DOWN, c.SDLK_s => Direction.Down,
            c.SDLK_LEFT, c.SDLK_a => Direction.Left,
            c.SDLK_RIGHT, c.SDLK_d => Direction.Right,
            else => Direction.None,
        });
    }

    fn render(self: *Self, renderer: *c.SDL_Renderer) void {
        const mo = &stage.attributes[self.mo];
        _ = c.SDL_RenderCopy(renderer, stage.textures[mo.texture],
            &c.SDL_Rect{
                .x = mo.whatever >> 7 << 4,
                .y = 0,
                .w = 16,
                .h = 16
            },
            &c.SDL_Rect{
                .x = @floatToInt(c_int, mo.position[0]),
                .y = @floatToInt(c_int, mo.position[1]),
                .w = 16,
                .h = 16
            });
    }
};

pub const Sprite = struct {
    texture: *c.SDL_Texture,
};

pub const Stage = struct {
    tick_now: f64,
    tick_last: f64,
    player: Player,
    attributes: [128]Attributes,
    textures: [64]?*c.SDL_Texture,

    const Self = @This();

    pub fn init() !Self {
        return Self{
            .tick_now = 0,
            .tick_last = 0,
            .player = undefined,
            .attributes = [_]Attributes{
                Attributes{
                    .enabled = false,
                    .position = undefined,
                    .speed = undefined,
                    .direction = undefined,
                    .texture = undefined,
                    .whatever = undefined,
                }
            } ** 128,
            .textures = [_]?*c.SDL_Texture{null} ** 64,
        };
    }

    pub fn deinit(self: *Self) void {
        for (self.textures) |texture| {
            if (texture) |t|
                c.SDL_DestroyTexture(t);
        }
    }

    pub fn update(self: *Self, delta: f64) void {
        self.player.update(delta);
        for (self.attributes) |*a| {
            a.position[0] += a.speed[0] * delta;
            a.position[1] += a.speed[1] * delta;
        }
    }

    pub fn render(self: *Self, renderer: *c.SDL_Renderer) void {
        self.player.render(renderer);

        for (self.attributes[1..]) |*a| {
            if (!a.enabled)
                continue;
//            warn("txtidx: {}\n", .{a.texture});
            _ = c.SDL_RenderCopy(renderer, stage.textures[a.texture],
                &c.SDL_Rect{
                    .x = 0,
                    .y = 0,
                    .w = 16,
                    .h = 16
                },
                &c.SDL_Rect{
                    .x = @floatToInt(c_int, a.position[0]),
                    .y = @floatToInt(c_int, a.position[1]),
                    .w = 16,
                    .h = 16
                });
        }
    }
};

A  => src/objman.zig +3 -0
@@ 1,3 @@
const std = @import("std");