package com.rpsg.rpg.controller; import java.util.ArrayList; import java.util.List; import com.badlogic.gdx.assets.AssetManager; import com.badlogic.gdx.graphics.Camera; import com.badlogic.gdx.graphics.OrthographicCamera; import com.badlogic.gdx.graphics.Texture.TextureFilter; import com.badlogic.gdx.graphics.g2d.Batch; import com.badlogic.gdx.maps.MapLayer; import com.badlogic.gdx.maps.tiled.TiledMap; import com.badlogic.gdx.maps.tiled.TiledMapTileLayer; import com.badlogic.gdx.maps.tiled.TmxMapLoader; import com.badlogic.gdx.maps.tiled.TmxMapLoader.Parameters; import com.badlogic.gdx.maps.tiled.renderers.OrthoCachedTiledMapRenderer; import com.badlogic.gdx.math.Matrix4; import com.badlogic.gdx.utils.viewport.ScalingViewport; import com.rpsg.gdxQuery.$; import com.rpsg.rpg.core.Game; import com.rpsg.rpg.core.Log; import com.rpsg.rpg.core.Path; import com.rpsg.rpg.core.Views; import com.rpsg.rpg.object.map.MapSprite; import com.rpsg.rpg.object.map.NPC; import com.rpsg.rpg.object.map.PlayerQueue; import com.rpsg.rpg.util.Timer; /** * 游戏地图({@link com.badlogic.gdx.maps.tiled.TiledMap TiledMap})控制器<br> */ public class MapController { /**当前的地图*/ TiledMap map; /**渲染器*/ OrthoCachedTiledMapRenderer render; /**相机*/ ScalingViewport viewport; /**地图资源*/ AssetManager assetManager; /**当前地图上所有的精灵*/ public List<MapSprite> mapSprites = new ArrayList<>(); /**脚本管理器*/ public MapScriptController script; boolean loaded = false; /**是否允许资源加载完成后就在屏幕画图,默认为true,他可以在JS脚本加载完毕后经由JS设置为false,这样可以在画图之前搞些事情*/ public boolean renderable = true; /**玩家队列*/ public PlayerQueue queue; /**地图名称*/ private String name; /**post*/ public PostController post; public MapController() { assetManager = new AssetManager(); /**增加一个TiledMap Loader给管理器*/ assetManager.setLoader(TiledMap.class, new TmxMapLoader()); post = new PostController(); } /** * 从硬盘中加载一张{@link TiledMap}地图 * @param path 地图路径 */ public void load(String path) { loaded = false; renderable = true; Views.loadView.start("load_tmx"); //如果不允许缓存就清除以前的地图 if(!Game.setting.cache) assetManager.clear(); Parameters param = new Parameters(); //使用Nearest过滤纹理,防止游戏窗口放大时,地图的每个小块中间出现缝隙 param.textureMinFilter = param.textureMagFilter = TextureFilter.Nearest; //加载完毕后的回调 param.loadedCallback = (am, fileName, type) -> { map = am.get(fileName, TiledMap.class); //销毁之前的render if(render != null) render.dispose(); //创建地图画笔 render = new OrthoCachedTiledMapRenderer(map); render.setBlending(true); //创建相机 viewport = Game.viewport(); //清空以前的地图精灵 this.mapSprites.clear(); //如果archiveMapSprites是null的话,表示从地图里载入精灵,否则从存档里载入精灵 List<MapSprite> archiveMapSprites = Game.archive.get().getMapSprites(); //依次把精灵(们)加入到舞台 this.mapSprites.addAll(archiveMapSprites == null ? loadSprites() : archiveMapSprites); //从存档中读取玩家坐标点 TODO queue = new PlayerQueue(); //创建脚本管理器 script = new MapScriptController(); Game.archive.get().mapName = path; Object _name = map.getProperties().get("name"); name = $.isEmpty(_name) ? "未知地图" : _name.toString(); resize(); loaded = true; Views.loadView.stop("load_tmx"); Log.i("A map loaded[" + fileName + "]"); }; //开始加载 assetManager.load(Path.MAP + path, TiledMap.class, param); } /** * 从map里读取并加载精灵 */ public List<MapSprite> loadSprites() { //测试用的代码 List<MapSprite> list = new ArrayList<>(); //创建一个NPC NPC npc = new NPC(0, 0, 0, Path.WALK_HERO + "walk_wriggle.png"); mapSprites.add(npc); //给这个NPC加上一条碰撞脚本 // npc.scripts.put(CollideType.face, Game.script.map.create("mytest.js")); //3秒之后执行一个假碰撞 // Timer.add(10, () -> script.add(npc, CollideType.face)); Timer.add(240, () -> npc.walk(MapSprite.Facing.RIGHT)); Timer.add(252, () -> npc.walk(MapSprite.Facing.RIGHT)); Timer.add(264, () -> npc.walk(MapSprite.Facing.RIGHT)); Timer.add(276, () -> npc.walk(MapSprite.Facing.UP)); Timer.add(288, () -> npc.walk(MapSprite.Facing.UP)); Timer.add(300, () -> npc.walk(MapSprite.Facing.UP)); Timer.add(312, () -> npc.walk(MapSprite.Facing.LEFT)); Timer.add(324, () -> npc.walk(MapSprite.Facing.LEFT)); Timer.add(336, () -> npc.walk(MapSprite.Facing.LEFT)); Timer.add(348, () -> npc.walk(MapSprite.Facing.DOWN)); Timer.add(360, () -> npc.walk(MapSprite.Facing.DOWN)); Timer.add(372, () -> npc.walk(MapSprite.Facing.DOWN)); Timer.add(400, () -> npc.setSpeed(1)); Timer.add(400 + 48 * 0, () -> npc.walk(MapSprite.Facing.RIGHT)); Timer.add(400 + 48 * 1, () -> npc.walk(MapSprite.Facing.RIGHT)); Timer.add(400 + 48 * 2, () -> npc.walk(MapSprite.Facing.RIGHT)); Timer.add(400 + 48 * 3, () -> npc.walk(MapSprite.Facing.UP)); Timer.add(400 + 48 * 4, () -> npc.walk(MapSprite.Facing.UP)); Timer.add(400 + 48 * 5, () -> npc.walk(MapSprite.Facing.UP)); Timer.add(400 + 48 * 6, () -> npc.walk(MapSprite.Facing.LEFT)); Timer.add(400 + 48 * 7, () -> npc.walk(MapSprite.Facing.LEFT)); Timer.add(400 + 48 * 8, () -> npc.walk(MapSprite.Facing.LEFT)); Timer.add(400 + 48 * 9, () -> npc.walk(MapSprite.Facing.DOWN)); Timer.add(400 + 48 * 10, () -> npc.walk(MapSprite.Facing.DOWN)); Timer.add(400 + 48 * 11, () -> npc.walk(MapSprite.Facing.DOWN)); //end 测试用的代码 return list; } /**瞬移到当前地图或某地图(取决mapName是否为空)*/ public void teleport(int x, int y, int z, String mapName) { Game.archive.get().position.set(x, y, z); if(mapName == null){//重置queue坐标 queue.reload(); }else{//重载地图 load(mapName); } } /** * 画图<br> * 画图将从最下层画到最顶层,并且如果有精灵的话,则在画图中穿插相应图层的精灵,精灵是在当前层之上画出的<br> * 穿插的精灵来自{@link #mapSprites}里的<br> * 画图时,{@link #render}将使用自己的画笔({@link com.badlogic.gdx.graphics.g2d.SpriteCache SpriteCache})进行画图,可能会导致画图异常(比如画不出其他的精灵)。 */ public void draw(Batch batch) { if(!loaded) return; post.begin(); Camera camera = viewport.getCamera(); camera.update(); //debug camera.position.set(730, 430, 0); //更新渲染器坐标位置,缓存画笔projection状态 Matrix4 projectionCpy = batch.getProjectionMatrix().cpy(); batch.setProjectionMatrix(camera.combined); //获取所有有效层 int layersCount = map.getLayers().getCount(); int skip = 0; //开始从最下层往上画 List<MapSprite> drawList = new ArrayList<>(10); for(int i = 0; i < layersCount; i ++){ MapLayer layer = map.getLayers().get(i); //如果该层不是块层,而是对象层,就跳过该层的绘制 if(!(layer instanceof TiledMapTileLayer)){ skip ++; continue; } //画地图 render.render(new int[]{i}); /** * 留给草刺猬爷爷:可能你第一个遇到的坑是各种坐标系问题=。= 研究一下以熟悉gdx吧 */ drawList.clear(); //遍历stage里所有的当前ZIndex的MapSprite并画出 for(MapSprite mapSprite : mapSprites) if(mapSprite.getZIndex() == i - skip) drawList.add(mapSprite); if(!drawList.isEmpty()){ batch.begin(); for(MapSprite sprite : drawList) sprite.draw(batch); batch.end(); } } //还原画笔projection状态 batch.setProjectionMatrix(projectionCpy); post.end(); } /**获取当前的地图*/ public TiledMap map() { return map; } /**每帧被执行*/ public void act() { assetManager.update(); if(!loaded) return; script.act(); for(MapSprite sprite : mapSprites) { sprite.act(); } } /**销毁地图控制器,将释放所有地图纹理等资源*/ public void dispose() { assetManager.dispose(); render.dispose(); post.dispose(); } /**返回当前地图的名称,如果没有则返回“未知地图”*/ public String name() { return name; } public boolean loaded() { return loaded; } public void resize() { if(viewport != null && render != null){ viewport.update(Game.width(), Game.height(), true); render.setView((OrthographicCamera)viewport.getCamera()); post.resize(viewport); } } }