日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Writing a Tile Engine in JavaFX

發(fā)布時間:2025/3/21 java 54 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Writing a Tile Engine in JavaFX 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

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

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

Artikel Navigation

Java Stammtisch in München

Writing a Tile Engine in JavaFX

Ver?ffentlicht am 9. Januar 2013

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的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 国模小丫大尺度啪啪人体 | 免费黄色三级网站 | 三级网站在线 | 中文字幕一区二区三区四区视频 | cao在线 | 黄色网址最新 | 激情五月婷婷综合网 | 91午夜理伦私人影院 | 日本中文字幕一区二区 | 婷婷深爱激情 | 亚洲天堂黄色 | 色狠狠综合| 日本福利一区二区 | 99热在线国产 | 精品国产午夜福利在线观看 | a级一a一级在线观看 | 爱乃なみ加勒比在线播放 | 欧美精品黑人猛交高潮 | 欧美日韩中文字幕在线观看 | 亚洲23p| 色人综合| 国产成人精品一区二区三区无码熬 | 91免费官网 | 婷婷开心激情网 | 精品66| 日本福利一区二区 | 加勒比在线一区 | 国产精品高潮呻吟久久 | 九九黄色片 | 欧美激情第三页 | 桃色一区 | 国产清纯白嫩初高中在线观看性色 | 奴色虐av一区二区三区 | 久久国产免费观看 | 狠狠综合网 | 精品视频不卡 | 亚洲av熟女国产一区二区性色 | 中文字幕精品无码亚 | 涩涩av| 国产主播在线播放 | 在线黄色免费 | 特黄aaaaaaa片免费视频 | 日韩大尺度视频 | 黄色成人影视 | 网爆门在线 | 中文字幕乱码人妻二区三区 | 亚洲国产日韩在线观看 | 日毛片 | 国产天堂在线观看 | 国产电影免费观看高清完整版视频 | 无遮挡边吃摸边吃奶边做 | 日韩在线播放一区二区 | 两个人看的www视频免费完整版 | 中文字幕免费看 | 天天视频污 | 日日夜夜免费 | 一区高清 | 久久aⅴ乱码一区二区三区 亚洲成人18 | 97色在线 | 一区二区精品在线观看 | 成人精品一区二区三区电影黑人 | 成人片黄网站色大片免费毛片 | 中文字幕亚洲日本 | 亚洲特黄视频 | 欧美伦理影院 | 日韩专区一区 | 国产美女无遮挡免费 | 成年女人色毛片 | 美女狂揉羞羞的视频 | 国产欧美在线播放 | 桃花岛影院 | 夜夜操夜夜操 | 韩国黄色av| 久久99网站 | 色婷婷av一区二区三区大白胸 | 91最新视频 | 老妇裸体性猛交视频 | 人妻一区二区三区 | 久久人人爽人人爽人人片av免费 | 床戏高潮做进去大尺度视频网站 | 久久小视频 | 国产成人无码精品亚洲 | 午夜小福利 | 国产精品久久毛片av大全日韩 | 精品国产一区二区三区久久久 | 乡村乱淫 | 欧美人妻一区二区 | 国产精品偷伦视频免费观看了 | 久久久国产精华液999999 | www.国产欧美| 久热这里有精品 | 羞羞网站在线观看 | 99re视频在线| 久久久久久久久久久网 | 亚洲精品久久久久久宅男 | 精品国产视频一区二区三区 | 丰满少妇xbxb毛片日本 | 日韩av在线第一页 | www.久久久.com |