0462-Bomb-实现双人模式

环境

  • Time 2024-03-23
  • 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

目标

实现双人模式,如果是双人模式,可以同时控制两个人。本次修改点较多,只列出重要的。

world.zig

生成地图时需要考虑第二个玩家,需要多分配一个内存。

    pub fn init(twoPlayer: bool, config: core.StageConfig) ?World {
        const number = config.enemy + @as(usize, if (twoPlayer) 2 else 1);
        const players = engine.allocator.alloc(Player, number) catch |e| {
            std.log.info("create players error: {}", .{e});
            return null;
        };

        var map = World{ .unit = core.getMapUnit(), .players = players };
        genMap(&map, twoPlayer, config);
        return map;
    }

play.zig

增加了对于玩家 2 的按键控制。

pub const Gameplay = struct {
    map: map.Map,
    mode: bool,

    pub fn init(mode: bool, level: usize) ?Gameplay {
        const m = map.Map.init(mode, level) orelse return null;
        return Gameplay{ .map = m, .mode = mode };
    }

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

        const speed = engine.frameTime() * playerSpeed;
        if (self.map.player1().alive) self.controlPlayer1(speed);
        if (self.map.player2().alive) self.controlPlayer2(speed);

        return null;
    }

    fn controlPlayer1(self: *Gameplay, speed: usize) void {
        if (engine.isDown(engine.Key.a))
            self.map.control(self.map.player1(), speed, .west);
        if (engine.isDown(engine.Key.d))
            self.map.control(self.map.player1(), speed, .east);
        if (engine.isDown(engine.Key.w))
            self.map.control(self.map.player1(), speed, .north);
        if (engine.isDown(engine.Key.s))
            self.map.control(self.map.player1(), speed, .south);

        if (engine.isPressed(engine.Key.space)) {
            self.map.setBomb(self.map.player1());
        }
    }

    fn controlPlayer2(self: *Gameplay, speed: usize) void {
        if (engine.isDown(engine.Key.j))
            self.map.control(self.map.player2(), speed, .west);
        if (engine.isDown(engine.Key.l))
            self.map.control(self.map.player2(), speed, .east);
        if (engine.isDown(engine.Key.i))
            self.map.control(self.map.player2(), speed, .north);
        if (engine.isDown(engine.Key.k))
            self.map.control(self.map.player2(), speed, .south);

        if (engine.isPressed(engine.Key.b)) {
            self.map.setBomb(self.map.player2());
        }
    }

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

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

map.zig

增加了第二个玩家的道具获取,死亡判断等。

const std = @import("std");
const engine = @import("engine.zig");
const core = @import("map/core.zig");
const world = @import("map/world.zig");
const ai = @import("map/ai.zig");

const Player = @import("map/player.zig").Player;

pub fn init() void {
    core.init();
}

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

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

pub const Map = struct {
    world: world.World,
    twoPlayer: bool,
    pub fn init(twoPlayer: bool, level: usize) ?Map {
        const wd = world.World.init(twoPlayer, stageConfig[level]);
        var initWorld = wd orelse return null;
        initWorld.players[0] = Player.genPlayer(1, 1, .player1);
        if (twoPlayer) {
            const w, const h = .{ initWorld.width - 2, initWorld.height - 2 };
            initWorld.players[1] = Player.genPlayer(w, h, .player2);
        }
        defer ai.init(initWorld);
        return Map{ .world = initWorld, .twoPlayer = twoPlayer };
    }

    pub fn update(self: *Map) void {
        self.getItem(self.player1());
        self.getItem(self.player2());

        for (self.world.players) |*p| {
            if (!p.alive) continue;
            const enemyPos = p.getCell();
            const unit = self.world.index(enemyPos.x, enemyPos.y);
            if (unit.hasExplosion()) p.alive = false;

            if (p.type != .enemy) continue;
            if (enemyPos.isSame(self.player1().getCell()))
                self.player1().alive = false;
            if (enemyPos.isSame(self.player2().getCell()))
                self.player2().alive = false;
        }

        self.world.update();
    }

    fn getItem(self: *Map, player: *Player) void {
        const playerPos = player.getCell();
        const mapUnit = self.world.indexRef(playerPos.x, playerPos.y);

        if (mapUnit.contains(.item)) {
            mapUnit.remove(.item);
            player.maxBombNumber += 1;
        }

        if (mapUnit.contains(.power)) {
            player.maxBombLength += 1;
            mapUnit.remove(.power);
        }
    }

    pub fn player1(self: Map) *Player {
        return self.world.player1();
    }

    pub fn player2(self: Map) *Player {
        return self.world.player2();
    }

    pub fn alive(self: Map) bool {
        return self.player1().alive or self.player2().alive;
    }

    pub fn control(self: Map, player: *Player, speed: usize, direction: core.Direction) void {
        if (direction == .west) {
            var p = player.*;
            p.x -|= speed;
            const cell = p.getCell();
            if (!self.world.isCollisionX(p, cell.x -| 1, cell.y))
                player.x -|= speed;
        }

        if (direction == .east) {
            var p = player.*;
            p.x += speed;
            const cell = p.getCell();
            if (!self.world.isCollisionX(p, cell.x + 1, cell.y))
                player.x +|= speed;
        }

        if (direction == .north) {
            var p = player.*;
            p.y -|= speed;
            const cell = p.getCell();
            if (!self.world.isCollisionY(p, cell.x, cell.y -| 1))
                player.y -|= speed;
        }

        if (direction == .south) {
            var p = player.*;
            p.y += speed;
            const cell = p.getCell();
            if (!self.world.isCollisionY(p, cell.x, cell.y + 1))
                player.y += speed;
        }
    }

    pub fn setBomb(self: *Map, player: *Player) void {
        if (player.bombNumber >= player.maxBombNumber) return;

        const pos = player.getCell();
        const cell = self.world.indexRef(pos.x, pos.y);
        if (!cell.contains(.wall) and !cell.contains(.brick)) {
            cell.insertTimedType(.bomb, engine.time());
            cell.insert(player.type);
            player.bombNumber += 1;
        }
    }

    pub fn draw(self: Map) void {
        self.world.draw();
    }

    pub fn deinit(self: *Map) void {
        ai.deinit();
        self.world.deinit();
    }
};

效果

bomb

总结

增加了对第二个玩家的支持。

附录

posted @ 2026-08-18 14:33  jiangbo4444  阅读(2)  评论(0)    收藏  举报