This commit is contained in:
wfz
2026-04-26 11:00:59 +08:00
parent 7bffe41d41
commit f1a6f0fd75
19 changed files with 1026 additions and 142 deletions

View File

@@ -3,6 +3,8 @@ package com.zombie.game.model;
import lombok.Getter;
import java.util.*;
import static com.zombie.game.model.Constants.*;
/**
* 子弹/投掷物类
*
@@ -123,7 +125,9 @@ public class Bullet {
int gx = (int) Math.floor(x);
int gy = (int) Math.floor(y);
if (map.isWall(gx, gy)) return false;
// 只有碰到静态墙壁才销毁子弹,碰到坚果墙体让碰撞检测处理
Wall wall = map.getWall(gx, gy);
if (wall instanceof StaticWall) return false;
return true;
}

View File

@@ -47,7 +47,7 @@ public class Constants {
/** 僵尸生成间隔基础值(秒),难度提升后会逐渐缩短 */
public static final float ZOMBIE_SPAWN_INTERVAL_BASE = 3.0f;
/** 僵尸生成间隔最小值(秒),防止生成过快 */
public static final float ZOMBIE_SPAWN_INTERVAL_MIN = 0.5f;
public static final float ZOMBIE_SPAWN_INTERVAL_MIN = 0.2f;
/** 每次难度提升增加的僵尸生命值 */
public static final float ZOMBIE_HEALTH_INCREASE = 20;
/** 每次难度提升增加的僵尸速度 */

View File

@@ -1,6 +1,9 @@
package com.zombie.game.model;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Getter;
import java.io.File;
import java.util.*;
/**
@@ -23,99 +26,97 @@ public class GameMap {
private final Map<String, Wall> walls;
/** 流场导航 */
private final FlowField flowField;
/** 玩家出生点 */
private final List<int[]> spawnPoints;
/** 僵尸出生点 */
private final List<int[]> zombieSpawnPoints;
/**
* 构造函数 - 初始化地图并生成默认布局
* 构造函数 - 从 JSON 文件加载地图
*
* @param mapFilePath 地图 JSON 文件路径
*/
public GameMap() {
this.width = Constants.GRID_SIZE;
this.height = Constants.GRID_SIZE;
public GameMap(String mapFilePath) {
JsonNode root = loadMapFile(mapFilePath);
this.width = root.get("width").asInt();
this.height = root.get("height").asInt();
this.cells = new int[height][width];
this.walls = new HashMap<>();
this.flowField = new FlowField(width, height);
generateDefaultMap();
this.spawnPoints = new ArrayList<>();
this.zombieSpawnPoints = new ArrayList<>();
loadFromJson(root);
}
/**
* 生成默认地图布局
*
* 创建边界墙壁、内部障碍物、玩家出生点和僵尸出生点
* 加载地图 JSON 文件
*/
private void generateDefaultMap() {
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
if (x == 0 || x == width - 1 || y == 0 || y == height - 1) {
cells[y][x] = 0;
private JsonNode loadMapFile(String mapFilePath) {
try {
ObjectMapper mapper = new ObjectMapper();
return mapper.readTree(new File(mapFilePath));
} catch (Exception e) {
throw new RuntimeException("Failed to load map file: " + mapFilePath, e);
}
}
/**
* 从 JSON 数据加载地图
*/
private void loadFromJson(JsonNode root) {
// 加载墙体
JsonNode wallsNode = root.get("walls");
if (wallsNode != null && wallsNode.isArray()) {
for (JsonNode wallNode : wallsNode) {
int x = (int) wallNode.get("x").asDouble();
int y = (int) wallNode.get("y").asDouble();
String type = wallNode.get("type").asText();
if (x < 0 || x >= width || y < 0 || y >= height) continue;
if ("static".equals(type)) {
walls.put(key(x, y), new StaticWall(x, y));
} else {
cells[y][x] = 0;
} else if ("nut".equals(type)) {
walls.put(key(x, y), new NutWall(x, y));
}
}
}
int[][] wallSegments = {
{5, 5, 5, 10}, {10, 3, 15, 3}, {20, 5, 20, 12},
{8, 15, 14, 15}, {25, 10, 25, 18}, {3, 20, 8, 20},
{15, 20, 15, 26}, {22, 22, 28, 22}, {5, 25, 10, 25},
{12, 8, 12, 12}, {18, 16, 22, 16}, {27, 5, 27, 9},
{8, 27, 13, 27}, {18, 26, 18, 30}
};
for (int[] seg : wallSegments) {
if (seg[0] == seg[2]) {
for (int y = seg[1]; y <= seg[3]; y++) {
if (seg[0] > 0 && seg[0] < width - 1 && y > 0 && y < height - 1) {
walls.put(key(seg[0], y), new StaticWall(seg[0], y));
}
}
} else {
for (int x = seg[0]; x <= seg[2]; x++) {
if (x > 0 && x < width - 1 && seg[1] > 0 && seg[1] < height - 1) {
walls.put(key(x, seg[1]), new StaticWall(x, seg[1]));
}
// 加载玩家出生点
JsonNode playerSpawns = root.get("playerSpawns");
if (playerSpawns != null && playerSpawns.isArray()) {
for (JsonNode spawn : playerSpawns) {
int x = spawn.get("x").asInt();
int y = spawn.get("y").asInt();
if (x >= 0 && x < width && y >= 0 && y < height) {
cells[y][x] = 2;
spawnPoints.add(new int[]{x, y});
}
}
}
int[][] spawnPoints = {{2, 2}, {29, 2}, {2, 29}, {29, 29}};
for (int[] sp : spawnPoints) {
cells[sp[1]][sp[0]] = 2;
for (int dy = -1; dy <= 1; dy++) {
for (int dx = -1; dx <= 1; dx++) {
int ny = sp[1] + dy, nx = sp[0] + dx;
if (ny > 0 && ny < height - 1 && nx > 0 && nx < width - 1) {
walls.remove(key(nx, ny));
}
}
}
}
int[][] zombieSpawnAreas = {{16, 2}, {2, 16}, {29, 16}, {16, 29}, {16, 16}};
for (int[] sp : zombieSpawnAreas) {
for (int dy = -1; dy <= 1; dy++) {
for (int dx = -1; dx <= 1; dx++) {
int ny = sp[1] + dy, nx = sp[0] + dx;
if (ny > 0 && ny < height - 1 && nx > 0 && nx < width - 1) {
walls.remove(key(nx, ny));
}
}
}
}
int[][] zombieSpawnPoints = {{8, 8}, {24, 24}};
for (int[] sp : zombieSpawnPoints) {
cells[sp[1]][sp[0]] = 3;
for (int dy = -1; dy <= 1; dy++) {
for (int dx = -1; dx <= 1; dx++) {
int ny = sp[1] + dy, nx = sp[0] + dx;
if (ny > 0 && ny < height - 1 && nx > 0 && nx < width - 1) {
walls.remove(key(nx, ny));
}
// 加载僵尸出生点
JsonNode zombieSpawns = root.get("zombieSpawns");
if (zombieSpawns != null && zombieSpawns.isArray()) {
for (JsonNode spawn : zombieSpawns) {
int x = spawn.get("x").asInt();
int y = spawn.get("y").asInt();
if (x >= 0 && x < width && y >= 0 && y < height) {
cells[y][x] = 3;
zombieSpawnPoints.add(new int[]{x, y});
}
}
}
}
/**
* 添加坚果墙体
*
* @param gx 格子X坐标
* @param gy 格子Y坐标
* @return true 表示添加成功
*/
/**
* 添加坚果墙体
*
@@ -201,8 +202,53 @@ public class GameMap {
return true;
}
/**
* 获取地图数据(包含墙体信息)
*
* 返回的二维数组格式与前端兼容:
* - 0 = 空地
* - 1 = 静态墙壁
* - 2 = 玩家出生点
* - 3 = 僵尸出生点
* - 4 = 坚果墙体
*
* @return 地图格子数据
*/
public int[][] getCells() {
return cells;
int[][] result = new int[height][width];
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
Wall wall = walls.get(key(x, y));
if (wall instanceof StaticWall) {
result[y][x] = 1;
} else if (wall instanceof NutWall) {
result[y][x] = 4;
} else {
result[y][x] = cells[y][x];
}
}
}
return result;
}
/**
* 获取所有坚果墙体的状态(用于同步给前端)
*
* @return 坚果墙体状态列表,每个元素包含 x, y, health, maxHealth
*/
public List<Map<String, Object>> getNutWallStates() {
List<Map<String, Object>> states = new ArrayList<>();
for (Wall wall : walls.values()) {
if (wall instanceof NutWall && !wall.isDestroyed()) {
Map<String, Object> state = new LinkedHashMap<>();
state.put("x", wall.getGridX());
state.put("y", wall.getGridY());
state.put("health", wall.getHealth());
state.put("maxHealth", NutWall.MAX_HEALTH);
states.add(state);
}
}
return states;
}
/**
@@ -228,12 +274,7 @@ public class GameMap {
* @return 僵尸出生点坐标列表
*/
public List<int[]> getZombieSpawnPoints() {
List<int[]> points = new ArrayList<>();
int[][] zombieSpawns = {{8, 8}, {24, 24}};
for (int[] sp : zombieSpawns) {
points.add(sp);
}
return points;
return zombieSpawnPoints;
}
/**

View File

@@ -3,6 +3,8 @@ package com.zombie.game.model;
import lombok.Getter;
import java.util.*;
import static com.zombie.game.model.Constants.*;
/**
* 游戏世界类
*
@@ -39,7 +41,11 @@ public class GameWorld {
private List<Integer> removedZombieBullets;
public GameWorld() {
this.map = new GameMap();
this("/Users/wfz/workspace/zp1/maps/d540209a.json");
}
public GameWorld(String mapFilePath) {
this.map = new GameMap(mapFilePath);
this.players = new LinkedHashMap<>();
this.zombies = new LinkedHashMap<>();
this.bullets = new LinkedHashMap<>();
@@ -206,17 +212,17 @@ public class GameWorld {
/**
* 更新所有僵尸
*
*
* 处理僵尸移动、攻击和死亡
*
*
* @param dt 时间增量(秒)
*/
private void updateZombies(float dt) {
long now = System.currentTimeMillis();
List<Zombie> sortedZombies = new ArrayList<>(zombies.values());
sortedZombies.sort((a, b) -> Integer.compare(a.getId(), b.getId()));
for (Zombie z : sortedZombies) {
if (!z.isAlive()) {
onZombieKilled(z);
@@ -235,7 +241,14 @@ public class GameWorld {
}
}
z.move(map, dt, zombies.values());
Wall attackedWall = z.move(map, dt, zombies.values(), now);
if (attackedWall != null && z.canAttack(now)) {
attackedWall.takeDamage(1.0f); // 每次攻击造成1点伤害
z.attack(now);
if (attackedWall.isDestroyed()) {
map.removeWall(attackedWall.getGridX(), attackedWall.getGridY());
}
}
}
}
@@ -278,19 +291,40 @@ public class GameWorld {
}
/**
* 检测僵尸子弹与玩家的碰撞
* 检测僵尸子弹与玩家/墙体的碰撞
*/
private void checkZombieBulletCollisions() {
List<Integer> bulletsToRemove = new ArrayList<>();
for (Bullet b : new ArrayList<>(zombieBullets.values())) {
boolean hit = false;
// 检测是否命中玩家
for (Player p : new ArrayList<>(players.values())) {
if (!p.isAlive()) continue;
if (b.hitsEntity(p.getX(), p.getY(), Constants.PLAYER_SIZE)) {
p.takeDamage(b.getDamage());
bulletsToRemove.add(b.getId());
hit = true;
break;
}
}
// 检测是否命中坚果墙体
if (!hit) {
int gx = (int) Math.floor(b.getX());
int gy = (int) Math.floor(b.getY());
Wall wall = map.getWall(gx, gy);
if (wall instanceof NutWall && !wall.isDestroyed()) {
wall.takeDamage(b.getDamage());
hit = true;
if (wall.isDestroyed()) {
map.removeWall(gx, gy);
}
}
}
if (hit) {
bulletsToRemove.add(b.getId());
}
}
for (int id : bulletsToRemove) {
zombieBullets.remove(id);
@@ -352,20 +386,41 @@ public class GameWorld {
}
/**
* 检测玩家子弹与僵尸的碰撞
* 检测玩家子弹与僵尸/墙体的碰撞
*/
private void checkBulletCollisions() {
List<Integer> bulletsToRemove = new ArrayList<>();
for (Bullet b : new ArrayList<>(bullets.values())) {
if (b.isGrenade()) continue;
// 检测是否命中僵尸
boolean hit = false;
for (Zombie z : new ArrayList<>(zombies.values())) {
if (!z.isAlive()) continue;
if (b.hitsEntity(z.getX(), z.getY(), Constants.ZOMBIE_SIZE)) {
z.takeDamage(b.getDamage());
bulletsToRemove.add(b.getId());
hit = true;
break;
}
}
// 检测是否命中坚果墙体
if (!hit) {
int gx = (int) Math.floor(b.getX());
int gy = (int) Math.floor(b.getY());
Wall wall = map.getWall(gx, gy);
if (wall instanceof NutWall && !wall.isDestroyed()) {
wall.takeDamage(b.getDamage());
hit = true;
if (wall.isDestroyed()) {
map.removeWall(gx, gy);
}
}
}
if (hit) {
bulletsToRemove.add(b.getId());
}
}
for (int id : bulletsToRemove) {
bullets.remove(id);
@@ -615,6 +670,7 @@ public class GameWorld {
state.put("explosions", new ArrayList<>(explosions));
state.put("removedBullets", new ArrayList<>(removedBullets));
state.put("nutWalls", map.getNutWallStates());
state.put("gameTime", gameTime);
state.put("waveNumber", waveNumber);
state.put("score", score);

View File

@@ -3,6 +3,8 @@ package com.zombie.game.model;
import lombok.Getter;
import java.util.*;
import static com.zombie.game.model.Constants.*;
/**
* 掉落物类
*

View File

@@ -0,0 +1,120 @@
package com.zombie.game.model;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.util.ArrayList;
/**
* 地图数据类
*
* 用于JSON序列化和反序列化
*/
public class MapData {
private String id;
private String name;
private int width;
private int height;
private List<Map<String, Object>> walls;
private List<Map<String, Integer>> playerSpawns;
private List<Map<String, Integer>> zombieSpawns;
public MapData() {
this.walls = new ArrayList<>();
this.playerSpawns = new ArrayList<>();
this.zombieSpawns = new ArrayList<>();
}
public MapData(String id, String name, int width, int height,
List<Map<String, Object>> walls,
List<Map<String, Integer>> playerSpawns,
List<Map<String, Integer>> zombieSpawns) {
this.id = id;
this.name = name;
this.width = width;
this.height = height;
this.walls = walls;
this.playerSpawns = playerSpawns;
this.zombieSpawns = zombieSpawns;
}
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getWidth() { return width; }
public void setWidth(int width) { this.width = width; }
public int getHeight() { return height; }
public void setHeight(int height) { this.height = height; }
public List<Map<String, Object>> getWalls() { return walls; }
public void setWalls(List<Map<String, Object>> walls) { this.walls = walls; }
public List<Map<String, Integer>> getPlayerSpawns() { return playerSpawns; }
public void setPlayerSpawns(List<Map<String, Integer>> playerSpawns) { this.playerSpawns = playerSpawns; }
public List<Map<String, Integer>> getZombieSpawns() { return zombieSpawns; }
public void setZombieSpawns(List<Map<String, Integer>> zombieSpawns) { this.zombieSpawns = zombieSpawns; }
public int[][] toCells() {
int[][] cells = new int[height][width];
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
cells[y][x] = 0;
}
}
for (Map<String, Object> wall : walls) {
int x = (Integer) wall.get("x");
int y = (Integer) wall.get("y");
if (x >= 0 && x < width && y >= 0 && y < height) {
cells[y][x] = "nut".equals(wall.get("type")) ? 2 : 1;
}
}
for (Map<String, Integer> spawn : playerSpawns) {
int x = spawn.get("x");
int y = spawn.get("y");
if (x >= 0 && x < width && y >= 0 && y < height) {
cells[y][x] = 2;
}
}
for (Map<String, Integer> spawn : zombieSpawns) {
int x = spawn.get("x");
int y = spawn.get("y");
if (x >= 0 && x < width && y >= 0 && y < height) {
cells[y][x] = 3;
}
}
return cells;
}
public static MapData fromGameMap(String id, String name, GameMap map) {
List<Map<String, Object>> walls = new ArrayList<>();
List<Map<String, Integer>> playerSpawns = new ArrayList<>();
List<Map<String, Integer>> zombieSpawns = new ArrayList<>();
for (Map.Entry<String, Wall> entry : map.getWalls().entrySet()) {
String[] parts = entry.getKey().split(",");
int x = Integer.parseInt(parts[0]);
int y = Integer.parseInt(parts[1]);
Wall wall = entry.getValue();
Map<String, Object> wallData = new HashMap<>();
wallData.put("x", x);
wallData.put("y", y);
wallData.put("type", wall instanceof NutWall ? "nut" : "static");
walls.add(wallData);
}
for (int[] spawn : map.getSpawnPoints()) {
Map<String, Integer> sp = new HashMap<>();
sp.put("x", spawn[0]);
sp.put("y", spawn[1]);
playerSpawns.add(sp);
}
for (int[] spawn : map.getZombieSpawnPoints()) {
Map<String, Integer> sp = new HashMap<>();
sp.put("x", spawn[0]);
sp.put("y", spawn[1]);
zombieSpawns.add(sp);
}
return new MapData(id, name, map.getWidth(), map.getHeight(), walls, playerSpawns, zombieSpawns);
}
}

View File

@@ -3,6 +3,8 @@ package com.zombie.game.model;
import lombok.Getter;
import java.util.*;
import static com.zombie.game.model.Constants.*;
/**
* 玩家类
*

View File

@@ -3,6 +3,8 @@ package com.zombie.game.model;
import lombok.Getter;
import java.util.*;
import static com.zombie.game.model.Constants.*;
/**
* 游戏房间类
*

View File

@@ -3,6 +3,8 @@ package com.zombie.game.model;
import lombok.Getter;
import java.util.*;
import static com.zombie.game.model.Constants.*;
/**
* 僵尸类
*
@@ -80,49 +82,84 @@ public class Zombie {
/**
* 移动僵尸
*
*
* 基于流场导航移动,包含:
* - 路径规划
* - 路径规划(支持加权障碍物,自动权衡绕道 vs 摧毁)
* - 避免与其他僵尸重叠
* - 墙壁碰撞检测
*
* - 攻击坚果墙体逻辑
*
* @param map 游戏地图
* @param dt 时间增量(秒)
* @param otherZombies 其他僵尸集合
* @param now 当前时间戳
* @return 如果正在攻击墙体,返回被攻击的墙体对象;否则返回 null
*/
public void move(GameMap map, float dt, Collection<Zombie> otherZombies) {
if (!map.isFlowFieldValid()) return;
public Wall move(GameMap map, float dt, Collection<Zombie> otherZombies, long now) {
if (!map.isFlowFieldValid()) return null;
int currentGridX = (int) Math.floor(x);
int currentGridY = (int) Math.floor(y);
// 如果正在攻击墙体,检查是否到达攻击位置
if (attackingWall && attackingWallGridX >= 0) {
float wallCenterX = attackingWallGridX + 0.5f;
float wallCenterY = attackingWallGridY + 0.5f;
float distToWall = distanceTo(wallCenterX, wallCenterY);
if (distToWall < 0.8f) {
// 到达攻击位置,返回要攻击的墙体
Wall wall = map.getWall(attackingWallGridX, attackingWallGridY);
if (wall != null && !wall.isDestroyed()) {
return wall;
}
// 墙体已被破坏或被移除,停止攻击
attackingWall = false;
attackingWallGridX = -1;
attackingWallGridY = -1;
}
}
float centerDist = Float.MAX_VALUE;
if (hasTarget) {
float dx = targetX - x;
float dy = targetY - y;
centerDist = (float) Math.sqrt(dx * dx + dy * dy);
}
if (!hasTarget || centerDist < 0.15f) {
float[] flowDir = map.getFlowDirection(x, y);
float dirX = flowDir[0];
float dirY = flowDir[1];
if (dirX == 0 && dirY == 0) return;
if (dirX == 0 && dirY == 0) return null;
float len = (float) Math.sqrt(dirX * dirX + dirY * dirY);
if (len > 0) {
dirX /= len;
dirY /= len;
}
int nextGridX = currentGridX + (int) Math.round(dirX);
int nextGridY = currentGridY + (int) Math.round(dirY);
if (map.isWall(nextGridX, nextGridY)) {
// 检查下一个格子是否是坚果墙体
if (map.isNutWall(nextGridX, nextGridY)) {
// 流场指引我们走向坚果,说明摧毁代价比绕道低
// 设置攻击状态
attackingWall = true;
attackingWallGridX = nextGridX;
attackingWallGridY = nextGridY;
reservedGridX = nextGridX;
reservedGridY = nextGridY;
reservation = true;
targetX = nextGridX + 0.5f;
targetY = nextGridY + 0.5f;
hasTarget = true;
} else if (map.isWall(nextGridX, nextGridY)) {
nextGridX = currentGridX + (int) Math.signum(dirX);
nextGridY = currentGridY + (int) Math.signum(dirY);
if (map.isWall(nextGridX, nextGridY)) {
if (!map.isWall(currentGridX + (int) Math.signum(dirX), currentGridY)) {
nextGridX = currentGridX + (int) Math.signum(dirX);
@@ -132,67 +169,86 @@ public class Zombie {
nextGridY = currentGridY + (int) Math.signum(dirY);
} else {
reservation = false;
return;
return null;
}
}
}
if (isGridOccupiedOrReserved(nextGridX, nextGridY, otherZombies)) {
int[] altDirs = findAlternativeDirection(currentGridX, currentGridY, dirX, dirY, map, otherZombies);
if (altDirs != null) {
nextGridX = altDirs[0];
nextGridY = altDirs[1];
} else {
reservation = false;
return;
if (isGridOccupiedOrReserved(nextGridX, nextGridY, otherZombies)) {
int[] altDirs = findAlternativeDirection(currentGridX, currentGridY, dirX, dirY, map, otherZombies);
if (altDirs != null) {
nextGridX = altDirs[0];
nextGridY = altDirs[1];
} else {
reservation = false;
return null;
}
}
reservedGridX = nextGridX;
reservedGridY = nextGridY;
reservation = true;
targetX = nextGridX + 0.5f;
targetY = nextGridY + 0.5f;
hasTarget = true;
} else {
if (isGridOccupiedOrReserved(nextGridX, nextGridY, otherZombies)) {
int[] altDirs = findAlternativeDirection(currentGridX, currentGridY, dirX, dirY, map, otherZombies);
if (altDirs != null) {
nextGridX = altDirs[0];
nextGridY = altDirs[1];
} else {
reservation = false;
return null;
}
}
reservedGridX = nextGridX;
reservedGridY = nextGridY;
reservation = true;
targetX = nextGridX + 0.5f;
targetY = nextGridY + 0.5f;
hasTarget = true;
}
reservedGridX = nextGridX;
reservedGridY = nextGridY;
reservation = true;
targetX = nextGridX + 0.5f;
targetY = nextGridY + 0.5f;
hasTarget = true;
}
float dx = targetX - x;
float dy = targetY - y;
float dist = (float) Math.sqrt(dx * dx + dy * dy);
if (dist < 0.01f) {
hasTarget = false;
return;
return null;
}
float dirX = dx / dist;
float dirY = dy / dist;
float moveX = dirX * speed * dt;
float moveY = dirY * speed * dt;
float newX = x + moveX;
float newY = y + moveY;
boolean canMoveX = map.isWalkable(newX, y, Constants.ZOMBIE_SIZE);
boolean canMoveY = map.isWalkable(x, newY, Constants.ZOMBIE_SIZE);
boolean canMoveDiagonal = map.isWalkable(newX, newY, Constants.ZOMBIE_SIZE);
if (moveX != 0 && moveY != 0) {
int checkX = (int) Math.floor(newX);
int checkY = (int) Math.floor(newY);
int checkCurrentX = (int) Math.floor(x);
int checkCurrentY = (int) Math.floor(y);
boolean blockedByCorner = false;
if (checkX != checkCurrentX && checkY != checkCurrentY) {
boolean wallInX = map.isWall(checkX, checkCurrentY);
boolean wallInY = map.isWall(checkCurrentX, checkY);
if (wallInX || wallInY) {
blockedByCorner = true;
if (!wallInX && canMoveX) {
x = newX;
canMoveY = map.isWalkable(x, newY, Constants.ZOMBIE_SIZE);
@@ -202,7 +258,7 @@ public class Zombie {
}
}
}
if (!blockedByCorner && canMoveDiagonal) {
x = newX;
y = newY;
@@ -214,26 +270,26 @@ public class Zombie {
if (canMoveX) x = newX;
if (canMoveY) y = newY;
}
float minSeparationDist = Constants.ZOMBIE_SIZE;
for (Zombie other : otherZombies) {
if (other.getId() == this.id) continue;
if (!other.isAlive()) continue;
float ox = other.getX();
float oy = other.getY();
float sepDx = x - ox;
float sepDy = y - oy;
float sepDist = (float) Math.sqrt(sepDx * sepDx + sepDy * sepDy);
if (sepDist < minSeparationDist && sepDist > 0.01f) {
float overlap = minSeparationDist - sepDist;
float pushX = (sepDx / sepDist) * overlap * 0.5f;
float pushY = (sepDy / sepDist) * overlap * 0.5f;
float pushedX = x + pushX;
float pushedY = y + pushY;
if (map.isWalkable(pushedX, pushedY, Constants.ZOMBIE_SIZE)) {
x = pushedX;
y = pushedY;
@@ -247,10 +303,12 @@ public class Zombie {
}
}
}
if (dirX != 0 || dirY != 0) {
angle = (float) Math.atan2(dirX, dirY);
}
return null;
}
/**