0452-Bomb-控制角色移动

环境

  • Time 2024-03-16
  • Zig 0.12.0-dev.3152+90c1a2c41
  • WSL-Ubuntu 22.04.3 LTS
  • raylib 5.0

前言

说明

参考资料:

  1. 《游戏开发:世嘉新人培训教材》
  2. 图片素材:https://www.ituring.com.cn/book/1742

目标

增加键盘控制,实现角色的移动。

TileMap

增加了一个根据实际的坐标来显示的方法 drawXY。

pub const TileMap = struct {
    texture: Texture,
    unit: usize,

    pub fn init(name: []const u8, unit: usize) TileMap {
        return .{ .texture = loadTexture(name), .unit = unit };
    }

    pub fn draw(self: TileMap) void {
        self.texture.draw();
    }

    pub fn drawI(self: TileMap, index: usize, x: usize, y: usize) void {
        const vec = .{ .x = x * self.unit, .y = y * self.unit };
        self.texture.drawRec(self.getRec(index), vec);
    }

    pub fn drawXY(self: TileMap, x: usize, y: usize, index: usize) void {
        self.texture.drawRec(self.getRec(index), .{ .x = x, .y = y });
    }

    fn getRec(self: TileMap, index: usize) basic.Rectangle {
        const rx = index * self.unit % self.texture.width;
        const ry = index / (self.texture.width / self.unit) * self.unit;
        return basic.Rectangle.init(rx, ry, self.unit, self.unit);
    }

    pub fn deinit(self: TileMap) void {
        self.texture.deinit();
    }
};

map.zig

修改了角色的坐标,从相对位置的坐标修改成了实际的界面坐标。

const std = @import("std");
const engine = @import("engine.zig");

var tileMap: engine.TileMap = undefined;

pub fn init() void {
    tileMap = engine.TileMap.init("map.png", 32);
}

pub fn deinit() void {
    tileMap.deinit();
}

const StageConfig = struct {
    enemy: usize,
    brickRate: usize,
    power: usize,
    bomb: usize,
};

const stageConfig = [_]StageConfig{
    .{ .enemy = 2, .brickRate = 90, .power = 4, .bomb = 6 },
    .{ .enemy = 3, .brickRate = 80, .power = 1, .bomb = 0 },
    .{ .enemy = 6, .brickRate = 30, .power = 0, .bomb = 1 },
};

// 定义地图的类型
pub const MapType = enum(u8) {
    space = 9,
    wall = 7,
    brick = 8,
    bomb = 2,
    power = 3,
};
const MapTypeSet = std.enums.EnumSet(MapType);

const width = 19;
const height = 15;
var data: [width * height]MapTypeSet = undefined;

pub fn drawEnum(mapType: MapType, x: usize, y: usize) void {
    tileMap.drawI(@intFromEnum(mapType), x, y);
}

const RoleType = enum(u8) { player1 = 1, player2 = 2, enemy = 6 };

pub const Role = struct {
    x: usize,
    y: usize,
    type: RoleType = .enemy,
};

pub const WorldMap = struct {
    width: usize = width,
    height: usize = height,
    data: []MapTypeSet,
    roles: []Role,

    pub fn init(_: usize) ?WorldMap {
        const number = stageConfig[0].enemy + 1;
        const roles = engine.allocator.alloc(Role, number) catch |e| {
            std.log.info("create role error: {}", .{e});
            return null;
        };

        roles[0] = .{
            .x = 1 * tileMap.unit,
            .y = 1 * tileMap.unit,
            .type = .player1,
        };

        const map = WorldMap{ .data = &data, .roles = roles };
        map.generateMap(stageConfig[0]);
        return map;
    }

    fn generateMap(self: WorldMap, config: StageConfig) void {
        var bricks: [data.len]usize = undefined;
        var brickNumber: usize = 0;
        var floors: [data.len]usize = undefined;
        var floorNumber: usize = 0;

        for (0..self.height) |y| {
            for (0..self.width) |x| {
                self.data[x + y * width] = if (isFixWall(x, y))
                    MapTypeSet.initOne(.wall)
                else if (isFixSpace(x, y))
                    MapTypeSet.initOne(.space)
                else if (engine.random(100) < config.brickRate) label: {
                    bricks[brickNumber] = x << 16 | y;
                    brickNumber += 1;
                    break :label MapTypeSet.initOne(.brick);
                } else label: {
                    floors[floorNumber] = x << 16 | y;
                    floorNumber += 1;
                    break :label MapTypeSet.initOne(.space);
                };
            }
        }
        generateItem(self, bricks[0..brickNumber], config);
        generateRole(self, floors[0..floorNumber], config);
    }

    fn isFixWall(x: usize, y: usize) bool {
        if (x == 0 or y == 0) return true;
        if (x == width - 1 or y == height - 1) return true;
        if (x % 2 == 0 and y % 2 == 0) return true;
        return false;
    }

    fn isFixSpace(x: usize, y: usize) bool {
        return y + x < 4;
    }

    fn generateItem(self: WorldMap, bricks: []usize, cfg: StageConfig) void {
        for (0..cfg.bomb + cfg.power) |i| {
            const swapped = engine.randomW(i, bricks.len);
            const tmp = bricks[i];
            bricks[i] = bricks[swapped];
            bricks[swapped] = tmp;
            const x = bricks[i] >> 16 & 0xFFFF;
            const item: MapType = if (i < cfg.power) .power else .bomb;
            self.data[x + (bricks[i] & 0xFFFF) * self.width].insert(item);
        }
    }

    fn generateRole(self: WorldMap, floors: []usize, cfg: StageConfig) void {
        for (0..cfg.enemy) |i| {
            const swapped = engine.randomW(i, floors.len);
            const tmp = floors[i];
            floors[i] = floors[swapped];
            floors[swapped] = tmp;
            self.roles[1 + i] = .{
                .x = (floors[i] >> 16 & 0xFFFF) * tileMap.unit,
                .y = (floors[i] & 0xFFFF) * tileMap.unit,
            };
        }
    }

    pub fn player1(self: WorldMap) *Role {
        return &self.roles[0];
    }

    pub fn draw(self: WorldMap) void {
        for (0..self.height) |y| {
            for (0..self.width) |x| {
                const value = data[x + y * self.width];
                if (value.contains(.wall)) drawEnum(.wall, x, y) //
                else if (value.contains(.brick)) drawEnum(.brick, x, y) //
                else {
                    drawEnum(.space, x, y);
                    if (value.contains(.power)) drawEnum(.power, x, y) //
                    else if (value.contains(.bomb)) drawEnum(.bomb, x, y);
                }
            }
        }

        for (self.roles) |v| {
            tileMap.drawXY(v.x, v.y, @intFromEnum(v.type));
        }
    }

    pub fn size(self: WorldMap) usize {
        return self.width * self.height;
    }

    pub fn deinit(self: WorldMap) void {
        engine.allocator.free(self.roles);
    }
};

play.zig

增加了玩家的控制

const std = @import("std");
const engine = @import("engine.zig");
const map = @import("map.zig");

pub const Gameplay = struct {
    map: map.WorldMap,

    pub fn update(self: *Gameplay) ?@import("popup.zig").PopupType {
        if (engine.isPressed(engine.Key.x)) return .over;
        if (engine.isPressed(engine.Key.c)) return .clear;

        const time = 1;
        var p1 = self.map.player1();
        if (engine.isDown(engine.Key.a)) p1.x -|= time;
        if (engine.isDown(engine.Key.d)) p1.x +|= time;
        if (engine.isDown(engine.Key.w)) p1.y -|= time;
        if (engine.isDown(engine.Key.s)) p1.y +|= time;

        return null;
    }

    pub fn draw(self: Gameplay) void {
        self.map.draw();
    }

    pub fn deinit(self: Gameplay) void {
        self.map.deinit();
    }
};

效果

bomb

总结

增加键盘控制,实现角色的移动。

附录

posted @ 2026-08-16 08:47  jiangbo4444  阅读(6)  评论(0)    收藏  举报