android游戲開發框架libgdx的使用(十四)—TiledMap中視角完善和障礙物處理


本文使用的libgdx是0.92版本,和現在的最新版可能有一些不一樣的地方。全文內容僅供參考。

本文緊跟上文:http://www.cnblogs.com/htynkn/archive/2012/01/13/libgdx_13.html

上文說到繪制了Map,然后我們的主角也可以四處活動了,但是仍有一些不完善的地方。

1.地圖的邊界沒有控制。Camera的位置其實是viewport的位置,不是屏幕邊界,所以如果直接按照上文的做法做的話主角走到屏幕邊緣的時候就有問題了。

zuobiao

2.沒有障礙,主角的行動沒有約束。

現在先來解決第一個問題。

解決方案很簡單,我們時刻注意viewport的位置,根據viewport計算Screen的邊界,讓其不超過地圖。

代碼如下:

private void CameraMove(Vector3 vector3, Actor mainActor) { 
Vector3 viewport = stage.getCamera().position.cpy();
viewport = viewport.add(vector3);
Vector3 zbound = new Vector3(width / 2, height / 2, 0).add(viewport);
if (zbound.x > maxCamPosition.x || zbound.y > maxCamPosition.y) {
return;
}
Vector3 fbound = new Vector3(-width / 2, -height / 2, 0).add(viewport);
if (fbound.x < 0 || fbound.y < 0) {
return;
}
stage.getCamera().position.add(vector3);
for (Actor actor : stage.getActors()) {
actor.x += vector3.x;
actor.y += vector3.y;
}
}

運行一下,恩,感覺還行。但是又有一個問題出現了…當地圖達到邊界時地圖不能滾動了,但是主角應該還是可以前進的。

13-1

處理方法我采用的是將Camera和主角分開處理,還是判斷一下,主角如果移動后不超出屏幕,就繼續移動。

Vector3 viewport = stage.getCamera().position.cpy(); 
viewport = viewport.add(vector3);
Vector3 zbound = new Vector3(width / 2, height / 2, 0).add(viewport);
if (zbound.x > maxCamPosition.x || zbound.y > maxCamPosition.y) {
isCameraMove = false;
}
Vector3 fbound = new Vector3(-width / 2, -height / 2, 0).add(viewport);
if (fbound.x < 0 || fbound.y < 0) {
isCameraMove = false;
}

Vector3 v3 = new Vector3(mainActor.x, mainActor.y, 0);
stage.getCamera().project(v3);
Vector3 a = v3.cpy().add(vector3);
if (a.x > width || a.y > height) {
isActorMove = false;
}
if (a.x < 0 || a.y < 0) {
isActorMove = false;
}

if (isCameraMove) {
stage.getCamera().position.add(vector3);
for (Actor actor : stage.getActors()) {
if (!actor.equals(player)) {
actor.x += vector3.x;
actor.y += vector3.y;
}
}
}
if (isActorMove) {
player.x += vector3.x;
player.y += vector3.y;
}



13-2

第一個問題基本解決,為什么說是基本解決?因為主角和Camera的位置可能會變動。造成主角在屏幕一角行走的問題。我一般是判斷主角位置,當位於中心區域時在讓二者聯動。

現在來解決第二個問題,障礙物的問題。

重新編輯我們的TMX文件。添加一個新圖塊:

metatiles

大小還是32*32.

13-3

然后新建一個圖層,將地圖中不能穿越的部分用紅色的方塊填充。

tilemap

編輯紅色塊的屬性,添加一個Pass-False鍵值(這個值是隨意的,只要你能懂就行)

13-4

最后隱藏該圖層,保存文件。(我在Editor里面確實隱藏了這個層,但是libgdx還是繪制了,只有自己處理一下了)

map = TiledLoader.createMap(mapHandle);

for (int i = 0; i < map.layers.size(); i++) {
if ("NoPass".equals(map.layers.get(i).name)) {
nopassLayer = map.layers.get(i);
map.layers.remove(i);
break;
}
}

map實例化以后,先尋找我們的障礙層,將其從map中移除。為了方便調試,暫時沒有移除它。

在主角每次移動時我們就檢查主角移動后的位置是在哪塊里面,這塊是不是不能通過的,如果不能就不移動,否則就移動。

先遍歷所有tiles,看一下pass=false的是多少ID的那塊。

       

  int nopassId = 0; 
TileSet set = map.tileSets.get(map.tileSets.size() - 1);
int masSize = set.firstgid + layer.tiles.length;
for (int i = 0; i < masSize; i++) {
if ("False".equals(map.getTileProperty(i, "Pass"))) {
nopassId = i;
Gdx.app.log("Find!", i + " ");
break;
}
}

 

然后推算移動后會處於哪塊中,哪一塊是不是不能通過的。

       

 int xid = MathUtils.ceilPositive(pos.x / map.tileWidth); 
int yid = MathUtils.ceilPositive(pos.y / map.tileWidth);
if (layer.tiles[layer.tiles.length - yid][xid - 1] == nopassId) {
return true;
} else {
return false;
}

 

在移動時先判斷一下,如果會達到不能到達的塊就直接退出。

      

  Vector2 pos = new Vector2(mainActor.x, mainActor.y); 
if (CheckMoveable(map, nopassLayer, vector3, pos)) {
return;
}

 

完整代碼:(代碼有點亂…)

package com.cnblogs.htynkn.game;

import javax.swing.text.ZoneView;
import javax.swing.text.html.MinimalHTMLWriter;

import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.InputMultiplexer;
import com.badlogic.gdx.InputProcessor;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.graphics.g2d.tiled.TileAtlas;
import com.badlogic.gdx.graphics.g2d.tiled.TileMapRenderer;
import com.badlogic.gdx.graphics.g2d.tiled.TileSet;
import com.badlogic.gdx.graphics.g2d.tiled.TiledLayer;
import com.badlogic.gdx.graphics.g2d.tiled.TiledLoader;
import com.badlogic.gdx.graphics.g2d.tiled.TiledMap;
import com.badlogic.gdx.graphics.g2d.tiled.TiledObject;
import com.badlogic.gdx.graphics.g2d.tiled.TiledObjectGroup;
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.Image;
import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.scenes.scene2d.ui.Label.LabelStyle;

public class JavaGame implements ApplicationListener, InputProcessor {

Stage stage;
float width;
float height;
private TiledMap map;
private TileAtlas atlas;
private TileMapRenderer tileMapRenderer;
Image player;
Vector3 camDirection = new Vector3(1, 1, 0);
Vector2 maxCamPosition = new Vector2(0, 0);
Vector3 moveVector = new Vector3(0, 0, 0);
boolean isPress;
TiledLayer nopassLayer;

// Image image;

@Override
public void create() {
final String path = "map/";
final String mapname = "tilemap";
FileHandle mapHandle = Gdx.files.internal(path + mapname + ".tmx");
map = TiledLoader.createMap(mapHandle);

for (int i = 0; i < map.layers.size(); i++) {
if ("NoPass".equals(map.layers.get(i).name)) {
nopassLayer = map.layers.get(i);
// map.layers.remove(i);
break;
}
}

atlas = new TileAtlas(map, new FileHandle("map/"));
tileMapRenderer = new TileMapRenderer(map, atlas, 10, 10);
maxCamPosition.set(tileMapRenderer.getMapWidthUnits(), tileMapRenderer
.getMapHeightUnits());

width = Gdx.graphics.getWidth();
height = Gdx.graphics.getHeight();
stage = new Stage(width, height, true);
Label label = new Label("FPS:", new LabelStyle(new BitmapFont(Gdx.files
.internal("font/blue.fnt"),
Gdx.files.internal("font/blue.png"), false), Color.WHITE),
"fpsLabel");
label.y = height - label.getPrefHeight();
label.x = 0;
stage.addActor(label);

for (TiledObjectGroup group : map.objectGroups) {
for (TiledObject object : group.objects) {
if ("play1".equals(object.name)) {
player = new Image(new TextureRegion(new Texture(Gdx.files
.internal("map/player.png")), 0, 0, 27, 40));
player.x = object.x;
player.y = tileMapRenderer.getMapHeightUnits() - object.y; // map是左上角,Stage是左下角
stage.addActor(player);
}
}
}

InputMultiplexer inputMultiplexer = new InputMultiplexer();
inputMultiplexer.addProcessor(this);
inputMultiplexer.addProcessor(stage);
Gdx.input.setInputProcessor(inputMultiplexer);
}

@Override
public void dispose() {
// TODO Auto-generated method stub

}

@Override
public void pause() {
// TODO Auto-generated method stub

}

@Override
public void render() {
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
OrthographicCamera c = (OrthographicCamera) stage.getCamera();
if (isPress) {
CameraMove(moveVector, player);
}
((Label) stage.findActor("fpsLabel")).setText("FPS: "
+ Gdx.graphics.getFramesPerSecond());
stage.act(Gdx.graphics.getDeltaTime());
tileMapRenderer.render(c);
stage.draw();
}

private void CameraMove(Vector3 vector3, Actor mainActor) {
boolean isCameraMove = true;
boolean isActorMove = true;

Vector2 pos = new Vector2(mainActor.x, mainActor.y);
if (CheckMoveable(map, nopassLayer, vector3, pos)) {
return;
}
if (CheckMoveable(map, nopassLayer, vector3, pos.cpy().add(
mainActor.width, 0))) {
return;
}
if (CheckMoveable(map, nopassLayer, vector3, pos.cpy().add(
mainActor.width, mainActor.height))) {
return;
}
if (CheckMoveable(map, nopassLayer, vector3, pos.cpy().add(0,
mainActor.height))) {
return;
}

Vector3 viewport = stage.getCamera().position.cpy();
viewport = viewport.add(vector3);
Vector3 zbound = new Vector3(width / 2, height / 2, 0).add(viewport);
if (zbound.x > maxCamPosition.x || zbound.y > maxCamPosition.y) {
isCameraMove = false;
}
Vector3 fbound = new Vector3(-width / 2, -height / 2, 0).add(viewport);
if (fbound.x < 0 || fbound.y < 0) {
isCameraMove = false;
}

Vector3 v3 = new Vector3(mainActor.x, mainActor.y, 0);
stage.getCamera().project(v3);
Vector3 a = v3.cpy().add(vector3);
if (a.x > width || a.y > height) {
isActorMove = false;
}
if (a.x < 0 || a.y < 0) {
isActorMove = false;
}

if (isCameraMove) {
stage.getCamera().position.add(vector3);
for (Actor actor : stage.getActors()) {
if (!actor.equals(player)) {
actor.x += vector3.x;
actor.y += vector3.y;
}
}
}
if (isActorMove) {
player.x += vector3.x;
player.y += vector3.y;
}
}

private boolean CheckMoveable(TiledMap map, TiledLayer layer,
Vector3 vector3, Vector2 playpos) {
Vector3 pos = new Vector3(playpos.x, playpos.y, 0).add(vector3);
int nopassId = 0;
TileSet set = map.tileSets.get(map.tileSets.size() - 1);
int masSize = set.firstgid + layer.tiles.length;
for (int i = 0; i < masSize; i++) {
if ("False".equals(map.getTileProperty(i, "Pass"))) {
nopassId = i;
Gdx.app.log("Find!", i + " ");
break;
}
}
int xid = MathUtils.ceilPositive(pos.x / map.tileWidth);
int yid = MathUtils.ceilPositive(pos.y / map.tileWidth);
if (layer.tiles[layer.tiles.length - yid][xid - 1] == nopassId) {
return true;
} else {
return false;
}
}

@Override
public void resize(int width, int height) {
// TODO Auto-generated method stub

}

@Override
public void resume() {
// TODO Auto-generated method stub

}

@Override
public boolean keyDown(int keycode) {

return false;
}

@Override
public boolean keyTyped(char character) {
// TODO Auto-generated method stub
return false;
}

@Override
public boolean keyUp(int keycode) {
// TODO Auto-generated method stub
return false;
}

@Override
public boolean scrolled(int amount) {
// TODO Auto-generated method stub
return false;
}

private void ChangeDirect(int typeId) {
switch (typeId) {
case 1:
moveVector.set(0, 1, 0);
Gdx.app.log("方向變動", "向上");
break;
case 2:
moveVector.set(0, -1, 0);
Gdx.app.log("方向變動", "向下");
break;
case 3:
moveVector.set(-1, 0, 0);
Gdx.app.log("方向變動", "向左");
break;
case 4:
moveVector.set(1, 0, 0);
Gdx.app.log("方向變動", "向右");
break;
}
}

@Override
public boolean touchDown(int x, int y, int pointer, int button) {
Vector3 tmp = new Vector3(x, y, 0);
stage.getCamera().unproject(tmp);
float newx = tmp.x - player.x;
float newy = tmp.y - player.y;
if (newx > 0 && newy > 0) {
if (newx > newy) {
ChangeDirect(4);
} else {
ChangeDirect(1);
}
} else if (newx > 0 && newy < 0) {
if (newx > -newy) {
ChangeDirect(4);
} else {
ChangeDirect(2);
}
} else if (newx < 0 && newy > 0) {
if (-newx > newy) {
ChangeDirect(3);
} else {
ChangeDirect(1);
}
} else {
if (-newx > -newy) {
ChangeDirect(3);
} else {
ChangeDirect(2);
}
}
isPress = true;
return false;
}

@Override
public boolean touchDragged(int x, int y, int pointer) {
// TODO Auto-generated method stub
return false;
}

@Override
public boolean touchMoved(int x, int y) {
// TODO Auto-generated method stub
return false;
}

@Override
public boolean touchUp(int x, int y, int pointer, int button) {
isPress = false;
Gdx.app.log("Info", "touchUp: x:" + x + " y: " + y + " pointer: "
+ pointer + " button: " + button);
return false;
}
}



最終效果:

13-5

 

寫在最后:

1.調試好了就要在繪制前把障礙層刪除。

2.注意各種坐標轉化。

3.如果需要變化地圖,直接操作Layer里面的那個二維數組。

 

本文用的檢測方法只是一種可行方案而已,也可以直接看角色占的塊數。

本文參考了:http://geekanddad.wordpress.com/2010/06/22/enemies-and-combat-how-to-make-a-tile-based-game-with-cocos2d-part-3/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM