0462-Bomb-实现双人模式
环境
- Time 2024-03-23
- Zig 0.12.0-dev.3152+90c1a2c41
- WSL-Ubuntu 22.04.3 LTS
- raylib 5.0
前言
说明
参考资料:
- 《游戏开发:世嘉新人培训教材》
- 图片素材: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();
}
};
效果

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

浙公网安备 33010602011771号