Fog of War¶
Fog of War on kontseptsioon, millega kirjeldatakse kaardil varjatud alasid. Seda kasutatakse sageli reaalaja strateegiamängudes, kuid ka käigupõhistes mängudes.
Paljud 4X (eXplore, eXpand, eXploit, eXterminate) mängud kaasaarvatud “Civilization” kasutavad “Fog of war” efekti.
"Fog of war" lisab mängule:
Ajendi mängukaarti avastada, et suurendada oma piiratud teadmisi selle kohta.
Realistlikkust: Simuleeritakse lahinguvälja, kus maastiku omadused ja
vaenlaste asukohad selguvad alles luure käigus. - Põnevust: Tundmatud piirkonnad avanevad järk-järgult, samal ajal kui vaenlaste positsioon ja väesuurus on vaid ajutised teadmised, mis võivad muutuda, kui mängija nähtavusalast lahkub.
"Dark chess" mängus näeb mängija vaid mänguruute, kuhu on lubatud oma malend viia.
Fog of War top-down tüüpi kaardil¶
Kaardi piiride loomiseks ja hiljem külastatud kohtade tähistamiseks
on vaja teada tileide mõõtmeid (pikkust ja laiust) ning nende koguarvu.
Selle teabe leiame TMX-faili algusest.
Need mõõtmed saab salvestada kas eraldi muutujatena:
int mapWidthint mapHeightint tileWidthint tileHeight
Või loome nende jaoks enumi klass:
public enum MapConfig {
MAP_WIDTH(20),
MAP_HEIGHT(15),
TILE_WIDTH(16),
TILE_HEIGHT(16);
private final int value;
MapConfig(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
Nüüd saab näiteks Player klassis defineerida kaardi piirid. Selleks loome
meetodi defineMapBounds ning kutsume seda meetodit välja Player klassi
konstruktoris. Piiride määramiseks kasutame ChainShape klassi, mis moodustab
suletud kontuuri. Selleks kasutame ChainShape liidese meetodit
createLoop, mis ühendab tipud suletud ahelaks.
private void defineMapBounds() {
BodyDef bdef = new BodyDef();
bdef.type = BodyDef.BodyType.StaticBody;
ChainShape shape = new ChainShape();
float rightDown = MapConfig.MAP_HEIGHT.getValue() *
MapConfig.TILE_HEIGHT.getValue() / MyGame.PPM;
float rightUp = MapConfig.MAP_WIDTH.getValue() *
MapConfig.TILE_WIDTH.getValue() / MyGame.PPM;
shape.createLoop(new float[]{
0, 0,
rightUp, 0,
rightUp, rightDown,
0, rightDown
});
FixtureDef fdef = new FixtureDef();
fdef.shape = shape;
world.createBody(bdef).createFixture(fdef);
shape.dispose();
}
Lisame Player klassi ka meetodid mängija x- ja y-koordinaatide saamiseks,
et neid kasutada põhimänguklassis (mis pärib Screen liidest),
“Fog of War”’i efekti tekitamiseks.
public float getX() {
return b2body.getPosition().x;
}
public float getY() {
return b2body.getPosition().y;
}
Lisame põhimänguklassi järgmised muutujad ning ka mapWidth, mapHeight,
tileWidth ja tileHeight muutujaid, kui ei loonud enum klassi.
private static final int VISION_RADIUS = 1; // määratlemiseks, kui mitut ``tile``\ ‘i mängija näeb igas suunas.
private boolean[][] visibilityMap; // näitamaks mis x- ja y-koordinaadi ``tile`` \’idel oleme käinud.
// int mapWidth;
// int mapHeight;
// int tileWidth = 16;
// int tileHeight = 16;
int mapWidth = MapConfig.MAP_WIDTH.getValue();
int mapHeight = MapConfig.MAP_HEIGHT.getValue();
int tileWidth = MapConfig.TILE_WIDTH.getValue();
int tileHeight = MapConfig.TILE_HEIGHT.getValue();
Põhimängukonstruktoris initsialiseerime visibilityMap muutuja vastavalt
kaardi mõõtmetele ning ka kaardimõõtmete muutujad kui me ei võta neid enumi
klassist.
public Maze(MyGame game) {
//this.mapWidth = map.getProperties().get("width",
Integer.class); // mul 20
//this.mapHeight = map.getProperties().get("height",
Integer.class); // mul 15
this.visibilityMap = new boolean[mapWidth][mapHeight];
}
renderFogOfWar meetodis kasutame ShapeRenderer klassi, et joonistada
mustad ristkülikud tile‘idele, kus mängija pole veel käinud.
private void renderFogOfWar() {
ShapeRenderer shapeRenderer = new ShapeRenderer();
shapeRenderer.setProjectionMatrix(camera.combined);
shapeRenderer.begin(ShapeRenderer.ShapeType.Filled);
for (int x = 0; x < mapWidth; x++) {
for (int y = 0; y < mapHeight; y++) {
if (!visibilityMap[x][y]) {
shapeRenderer.setColor(0.0f, 0.0f, 0.0f, 0.1f);
shapeRenderer.rect(x * tileWidth / game.getPPM(), y
* tileHeight / game.getPPM(),
tileWidth / game.getPPM(), tileHeight / game.getPPM());
}
}
}
shapeRenderer.end();
shapeRenderer.dispose();
}
updateFogOfWar meetodis uuendame tile‘ide nähtavust mängija hetkese
x- ja y-koordinaati järgi nii kaugele, kui on määratud VISION_RADIUS põhjal.
private void updateFogOfWar(float playerX, float playerY) {
int tileX = (int) (playerX * game.getPPM() / tileWidth);
int tileY = (int) (playerY * game.getPPM() / tileHeight);
for (int x = tileX - VISION_RADIUS; x <= tileX + VISION_RADIUS; x++) {
for (int y = tileY - VISION_RADIUS; y <= tileY + VISION_RADIUS;
y++) {
if (x >= 0 && x < visibilityMap.length && y >= 0
&& y < visibilityMap[0].length) {
double distance = Math.sqrt((tileX - x) * (tileX - x) +
(tileY - y) * (tileY - y));
if (distance <= VISION_RADIUS) {
visibilityMap[x][y] = true;
}
}
}
}
}
render funktsiooni on nüüd vaja lisada nii updateFogOfWar kui ka
renderFogOfWar meetodi välja kutsumine. Kuna SpriteBatch ja
ShapeRenderer ei saa korraga aktiivsed olla (SpriteBatch joonistab
tekstuure ja ShapeRenderer kujundeid), peame renderFogOfWar meetodi
välja kutsuma peale batch.end toimingut.
@Override
public void render(float delta) {
updateFogOfWar(player.getX(), player.getY());
// vahepeal on batch.begin() ja batch.end()
renderFogOfWar();
}