http://jayskills.com/blog/2013/01/09/writing-a-tile-engine-in-javafx/

————————————————————————————————————————————————————————————————————

Artikel Navigation

Writing a Tile Engine in JavaFX

With the advent of embedded versions of JavaFX, our framework has become more interesting for game development, since we now can target small consumer devices like tablets & smartphones. So I decided to do a little more experimenting with JavaFX for writing Games. This time I wanted to use Canvas to have more control over the rendering in order to be able to optimize the performance on smaller devices. These are my experiences when writing a Tile Engine.

What’s a Tile Engine?

Back in the early days game consoles & computers had very limited resources. So in order to have games with thousands of large screens developers needed to come up with a method to store the screens in a format other than a bitmap per screen. So Tile Engines were invented that can generate large screens from a limited set of re-usable smaller graphics (Tiles). This saves ram and improves rendering performance.

TileMaps

The instructions how to generate the screen are stored in TileMaps. Those maps are typically organized as a 2-dimensional matrix of Tile ids. Usually the tiles are organized in layers to allow for a simple Z-ordering and more flexibility in combining graphics with different backgrounds. Usually TileMaps also support storing of meta data, for example if certain tiles are blocked, or spawn points for enemies.

A TileMap with several layers created with the

TileSets

The tiles referenced in the map are usually stored in TileSets that consist of a single bitmap and meta information about how to divide it into tiles. Here’s an example of such an image from opengameart.com, a site that hosts game assets with Open Source Licences. In my examples I use some of these graphics.

A typical TileSet Image sized 1024 x 1024 (^2 = good for graphics cards)

ObjectGroups

One additional feature of the TMX format are Object Layers. These special layers can be used to define freeform shapes and polylines and assign properties to them. The basic idea behind that is that we can use them to define areas where Sprites are created (spawnpoints), exits, portals, and non-rectangular collision shapes. It’s up to the creator of the TileEngine or the developer who builds games with it to define how to handle the ObjectGroups. I’m planning to use them extensively, and they are a very nice extension point for declaratively defining the gameplay. You can e.g. use them to define animations, skript dialogs, etc..

Spawnpoints defined in the

Workflow, Tools & Formats

The idea of tilemaps also allows for a nice workflow. Graphic designers can create the assets and game designers can import them into a level editor like “Tiled” and design the levels via drag & drop. The maps are stored in a machine readable TileMap format. Tiled for example uses the TMX Map format for storing the TileMap. That’s a very simple XML format, that can then be loaded by the TileEngine. For my implementation I decided to use the TMX Format, so I can use “Tiled” for designing the levels.

Implementation in JavaFX

For the implementation I decided to use JavaFX Canvas immediate mode rendering instead of the retained mode rendering when using individual Nodes. This gives me a bit more control for optimizing the performance on small devices like the Raspberry Pi.

Reading TMX/TSX files

The first thing we need is a way to read the TileMap (TMX) and TileSet (TSX) files. With JAXB it’s pretty simple to create a TileMapReader that can create POJOs from a file. So if you use the Engine you simply call:

TileMap map = TileMapReader.readMap(“path/to/my/map.tmx”);

The Camera

Since in most games the TileMaps will be larger than the screen, only a part of the Map is rendered. Usually the map is centered on the hero. You can do that by simply tracking the map position of upper left corner of the screen. We refer to this as our Camera position. The position is then updated from the hero’s position just before the TileMap is rendered like this:

// the center of the screen is the preferred location of our herodouble centerX = screenWidth / 2;double centerY = screenHeight / 2;cameraX = hero.getX() - centerX;cameraY = hero.getY() - centerY;

We just need to make sure the camera doesn’t leave the tilemap:

// if we get too close to the bordersif (cameraX >= cameraMaxX) {cameraX = cameraMaxX;}if (cameraY >= cameraMaxY) {cameraY = cameraMaxY;}

Rendering the TileMap using Canvas

Then it’s really easy to render the tiles. we simply loop through the layers and ask the tilemap to render the correct image at the current position. First we need to find out which tiles are currently visible, and the offset, since our hero moves pixel by pixel, not tile by tile:

// x,y index of first tile to be shownint startX = (int) (cameraX / tileWidth);int startY = (int) (cameraY / tileHeight);// the offset in pixelsint offX = (int) (cameraX % tileWidth);int offY = (int) (cameraY % tileHeight);Then we loop through the visible layers and draw the tile:for (int y = 0; y < screenHeightInTiles; y++) {for (int x = 0; x < screenWidthInTiles; x++) {// get the tile id of the tile at this positionint gid = layer.getGid((x + startX) + ((y + startY) * tileMap.getWidth()));graphicsContext2D.save();// position the graphicscontext for drawinggraphicsContext2D.translate((x * tileWidth) - offX, (y * tileHeight) - offY);// ask the tilemap to draw the tiletileMap.drawTile(graphicsContext2D, gid);// restore the old stategraphicsContext2D.restore();}}

The TileMap will then find out which Tileset the Tile belongs to and ask the TileSet to draw it to the Context. Drawing itself is as simple as finding the correct coordinates in your TileSets Image:

public void drawTile(GraphicsContext graphicsContext2D, int tileIndex) {int x = tileIndex % cols;int y = tileIndex / cols;// TODO support for margin and spacinggraphicsContext2D.drawImage(tileImage, x * tilewidth, y* tileheight, tilewidth, tileheight, 0, 0, tilewidth, tileheight);}

Game Loop. So we can simplify it to:

The Game Loop is again very simple. I’m using a TimeLine and a KeyFrame to fire a pulse for the game at a certain framerate (FPS):

final Duration oneFrameAmt = Duration.millis(1000 / FPS);final KeyFrame oneFrame = new KeyFrame(oneFrameAmt,new EventHandler() {@Overridepublic void handle(Event t) {update();render();}});TimelineBuilder.create().cycleCount(Animation.INDEFINITE).keyFrames(oneFrame).build().play();

Sprites

Every call to update in the TileMapCanvas loops through all Sprites and updates them. Basic Sprites currently contain one TileSet with a walkcycle like this:

Since sprites typically have a lot of transparent space around them, in order to have some extra room for animated behavior like like swinging a sword, I decided to allow to add a MoveBox and a CollisionBox for convenience. The CollisionBox can be used to define an area where our hero can be hurt. The MoveBox should be placed around the legs, so it can pass in front of forbidden tiles while the upper body is overlapping the tile. The blueish area around our “hero” is the sprite boundary:

https://www.youtube.com/watch?v=08H6LZkcqXw

Sprites can also have a timed Behavior. On every update the Sprite loops through it’s behaviors and checks if it’s time to fire. If so it’s “behave” method is called. If we have an enemy, like the skeleton in the sample app, we can add it’s AI here. Our Skeleton has for example a very simple behavior to make it follow our hero. It also checks for collision and causes damage to our hero like that:

monsterSprite.addBehaviour(new Sprite.Behavior() {@Overridepublic void behave(Sprite sprite, TileMapCanvas playingField) {if (sprite.getCollisionBox().intersects(hero.getCollisionBox())) {hero.hurt(1);}}});

The default interval is a second. If you need other intervals you can set them. Behaviors are reusable, different sprites can share the same Behavior instance. Behaviors are similar to KeyFrames, and I’m currently also using them to time the Animations (increase the tile index for the next render call).

ObjectGroupHandler

As mentioned in the beginning ObjectGroups are handy extension points. In my example game I use them for defining the spawn points of our hero and the monsters. Currently you simply add an ObjectGroupHandler which in turn uses the information in the ObjectGroup to create the Hero and Monster sprites and add Behavior to them:

class MonsterHandler implements ObjectGroupHandler {Sprite hero;@Overridepublic void handle(ObjectGroup group, final TileMapCanvas field) {if (group.getName().equals("sprites")) {for (TObject tObject : group.getObjectLIst()) {if (tObject.getName().equals("MonsterSpawner")) {try {double x = tObject.getX();double y = tObject.getY();TileSet monster = TileMapReader.readSet("/de/eppleton/tileengine/resources/maps/BODY_skeleton.tsx");Sprite monsterSprite = new Sprite(monster, 9, x, y, "monster");monsterSprite.setMoveBox(new Rectangle2D(18, 42, 28, 20));field.addSprite(monsterSprite);monsterSprite.addBehaviour(new Sprite.Behavior() {@Overridepublic void behave(Sprite sprite, TileMapCanvas playingField) {if (sprite.getCollisionBox().intersects(hero.getCollisionBox())) {hero.hurt(1);}}});}

Putting it all together

To create a sample game all you need to do is create TileMaps, TileSets, one or more ObjectGroupHandler(s) to create the Sprites and add Behavior, and you’re ready to play:

// create the worldTileMap tileMap = TileMapReader.readMap("/de/eppleton/tileengine/resources/maps/sample.tmx");// initialize the TileMapCanvasTileMapCanvas playingField = new TileMapCanvas(tileMap, 0, 0, 500, 500);// add Handlers, can also be done declaratively.playingField.addObjectGroupHandler(new MonsterHandler());// display the TileMapCanvasStackPane root = new StackPane();root.getChildren().add(playingField);Scene scene = new Scene(root, 500, 500);playingField.requestFocus();primaryStage.setTitle("Tile Engine Sample");primaryStage.setScene(scene);primaryStage.show();

That was the starting point of my Tile Engine. In the meantime it has evolved a bit into a more general purpose 2D-engine, so also Sprites that are not using TileSets and Layers that are freely rendered are supported. But it works pretty well so far.

Dieser Eintrag wurde in Allgemein,javafx geschrieben. Link merken.

Writing a Tile Engine in JavaFX相关推荐

  1. Isometric Tile Engine 的遮挡处理

    [url]http://www.codingnow.com/isomtric/isotile.htm[/url] 所谓 Isometric, 应该是指等距视角, 和透视相对, 指视野内的物体, 无论远 ...

  2. idea 编写javafx_用JavaFX编写图块引擎

    idea 编写javafx 随着JavaFX嵌入式版本的问世,我们的框架对于游戏开发变得越来越有趣,因为我们现在可以瞄准平板电脑和智能手机等小型消费类设备. 因此,我决定对JavaFX进行更多的游戏编 ...

  3. 用JavaFX编写图块引擎

    随着JavaFX嵌入式版本的问世,我们的框架对于游戏开发变得越来越有趣,因为我们现在可以瞄准平板电脑和智能手机等小型消费类设备. 因此,我决定对JavaFX进行更多的游戏编写实验. 这次,我想使用Ca ...

  4. javafx 教程_用JavaFX编写图块引擎

    javafx 教程 随着JavaFX嵌入式版本的问世,我们的框架对于游戏开发变得越来越有趣,因为我们现在可以瞄准平板电脑和智能手机等小型消费类设备. 因此,我决定对JavaFX进行更多的游戏编写实验. ...

  5. 使用Direct3D渲染2D图素

    > >    使用Direct3D渲染2D图素 2001-09-22   中国游戏开发者.CN 图片及源代码请登陆下面网站: 合作翻译: 中国游戏开发者.CN   –   mays htt ...

  6. node seneca_使用Node.js和Seneca编写国际象棋微服务,第3部分

    node seneca Finishing up a three-part series on writing a rules engine with Seneca microservices. 完成 ...

  7. 转载:Windows Phone 7 资源汇总(超全)

    一些关于WP7开发的资源,记录下来,很受用. 工具,书籍,指南 Windows Phone 7 Developer Tools RTM (online installer) , ISO UI Desi ...

  8. [转]结合php5与zend_search_lucene来创建一个全文搜索引擎

    Creating a fulltext search engine in PHP 5 with the Zend Framework's Zend_Search_Lucene --http://www ...

  9. 【Cherno的OpenGL视频】Welcome to OpenGL

    Let's talk about OpenGL, in this video we're basically just going to cover what OpenGL actual is? Ho ...

最新文章

  1. 从 301 跳转,聊聊边缘规则的那些小妙用
  2. OneData建设探索之路:SaaS收银运营数仓建设
  3. 了解 C# foreach 内部语句和使用 yield 实现的自定义迭代器
  4. import _ssl # if we can‘t import it, let the error propagate
  5. ASP.NET MVC Filter过滤机制(过滤器、拦截器)
  6. 王爽 汇编语言第三版 课程设计 1
  7. linux 桌面使用体验 远程访问win for linux
  8. 2019/2/17 Python今日收获
  9. 【MySQL联合查询】内连接查询详解
  10. 433MHz LoRa/FSK 无线频谱波形分析(频谱分析仪测试LoRa/FSK带宽、功率、频率误差等)
  11. 命令行安装卸载驱动服务
  12. 分子量-算法竞赛习题3-2:给出一种物质的分子式(不带括号),求分子量。本题中的分子式只包含4种原子,分别为C, H, O, N,原子量分别为12.01, 1.008, 16.00, 14.01。
  13. 企业服务总线 ESB 介绍和用例
  14. AndroidX适配教程
  15. linux 温度测试软件,linux上安装cpu温度监控软件
  16. ecshop与java的关系_ecshop模板的原理分析
  17. Bootstrap系列之表单(Forms)
  18. EasyCVR平台如何实现超低延时的安防视频监控直播?
  19. 在线转换pdf和虚拟打印机生成pdf文件操作攻略
  20. Java项目:游戏点评系统(java+SSM+JSP+JavaScript+mysql)

热门文章

  1. 低差别序列:高维序列(Halton sequence)
  2. 如何正确运用计算机,如何正确使用电脑
  3. 怎么测内阻 恒压源_测电压表内阻的六种方法
  4. 大数据和python哪个好_大数据语言之争:Java和python哪个好?
  5. python通过pyinstaller打包软件将GUI项目打包成exe文件
  6. python gui打包exe pyinstaller打包运行失败 Failed to execute script pyi_rth_multiprocessing
  7. oracle mysql 并发连接数_如何修改Oracle并发连接数的设置
  8. vue 引入swiper4,在有些手机Dom7会有问题
  9. android中的标题栏是什么意思,Android通用标题栏组合控件
  10. mongodb java 学习_《 mongodb 学习 》java 基本操作