Second version of the my free tileset. Now is possible to build a entire house. :-)
Here is the tileset.
There’s a nice map editor with several features called Tiled. If you are edditing game maps, take a look.
Second version of the my free tileset. Now is possible to build a entire house. :-)
Here is the tileset.
There’s a nice map editor with several features called Tiled. If you are edditing game maps, take a look.
I started to make several small JavaFX game demos. I’m doing that to fell where JavaFX is good to make this sort of game and what patterns would be frequently needed to implement, where I will place a little framework for fast development of simple casual games. What I’m calling now just ‘GameFX’. My first experiment was to creating a side scrolling animation that’s is usefull to create the parallax effect in side scrolling games. For that I created the class Slidding. You create an Slidding with a set of nodes and they will slide from right to left and when a node reaches the left side it back to the right side.
Example:
Slidding { content: [ Circle { centerX: 100, centerY: 100, radius: 40 fill: Color.RED }, Circle { centerX: 300, centerY: 100, radius: 40 fill: Color.BLUE } ] clock: 0.05s } |
That produces:
You create a Slidding with a list of Nodes at content, a clock (that will determine the speed of that animation) and a width. If you don’t provide a width, the slidding will do the best effort to determine one. You can use this approach to create more complex scenarios, using more Slidding groups.
This is a example of that:
import javafx.application.*; import javafx.animation.*; import javafx.scene.geometry.*; import javafx.scene.paint.*; import javafx.scene.*; import gamefx.Slidding; var SCREENW = 500; var SCREENH = 400; /* the sky is a light blue rectangle */ var sky = Rectangle { width: SCREENW, height: SCREENH fill: LinearGradient { startX: 0.0 , startY: 0.0 endX: 0.0, endY: 1.0 proportional: true stops: [ Stop { offset: 0.0 color: Color.LIGHTBLUE }, Stop { offset: 0.7 color: Color.LIGHTYELLOW }, Stop { offset: 1.0 color: Color.YELLOW } ] } } /* the ground is a olive rectangle */ var ground = Rectangle { translateY: 300 width: 500, height: 100 fill: LinearGradient { startX: 0.0 , startY: 0.0 endX: 0.0, endY: 1.0 proportional: true stops: [ Stop { offset: 0.2 color: Color.OLIVE }, Stop { offset: 1.0 color: Color.DARKOLIVEGREEN } ] } } /* a clod cloud is like an ellipse */ class Cloud extends Ellipse { override attribute radiusX = 50; override attribute radiusY = 25; override attribute fill = Color.WHITESMOKE; override attribute opacity = 0.5; } /* we create a slidding of clouds */ var clouds = Slidding { content: [ Cloud{centerX: 100, centerY: 100}, Cloud{centerX: 150, centerY: 20}, Cloud{centerX: 220, centerY: 150}, Cloud{centerX: 260, centerY: 200}, Cloud{centerX: 310, centerY: 40}, Cloud{centerX: 390, centerY: 150}, Cloud{centerX: 450, centerY: 30}, Cloud{centerX: 550, centerY: 100}, ] clock: 0.2s } var SUNX = 100; var SUNY = 300; var rotation = 0; /* the sun, with it's corona */ var sun = Group { rotate: bind rotation anchorX: SUNX, anchorY: SUNY content: [ for (i in [0..11]) { Arc { centerX: SUNX, centerY: SUNY radiusX: 500, radiusY: 500 startAngle: 2 * i * (360 / 24), length: 360 / 24 type: ArcType.ROUND fill: Color.YELLOW opacity: 0.3 } }, Circle { centerX: SUNX, centerY: SUNY, radius: 60 fill: Color.YELLOW }, ] } /* animate the corona changing the it rotation angle */ var anim = Timeline { repeatCount: Timeline.INDEFINITE keyFrames : [ KeyFrame { time : 0s values: rotation => 0.0 tween Interpolator.LINEAR }, KeyFrame { time : 2s values: rotation => (360.0/12) tween Interpolator.LINEAR }, ] } anim.start(); /* a tree is a simple polygon */ class Tree extends Polygon{ public attribute x = 0; public attribute y = 0; override attribute points = [0,0, 10,30, -10,30]; override attribute fill = Color.DARKOLIVEGREEN; init{ translateX = x; translateY = y; } } /* a forest is a lot of trees */ var forest = Slidding{ content: [ Tree{x: 20, y: 320}, Tree{x: 80, y: 280}, Tree{x:120, y: 330}, Tree{x:140, y: 280}, Tree{x:180, y: 310}, Tree{x:220, y: 320}, Tree{x:260, y: 280}, Tree{x:280, y: 320}, Tree{x:300, y: 300}, Tree{x:400, y: 320}, Tree{x:500, y: 280}, Tree{x:500, y: 320} ] clock: 0.1s width: SCREENW } Frame { title: "Side Scrolling" width: SCREENW height: SCREENH closeAction: function() { java.lang.System.exit( 0 ); } visible: true stage: Stage { content: [sky, sun, clouds, ground, forest] } } |
Producing:
If you want to try these examples, place this Slidding implementation as Slidding.fx in a directory named gamefx, or grab here the NetBeans project.
package gamefx; import javafx.scene.CustomNode; import javafx.scene.Node; import javafx.scene.Group; import javafx.animation.Timeline; import javafx.animation.KeyFrame; /* * The slidding group of nodes for side scrolling animations. * * @example * Slidding { * width: 300 * content: [ * Circle { centerX: 100, centerY: 100, radius: 40, fill: Color.RED }, * Circle { centerX: 200, centerY: 100, radius: 40, fill: Color.BLUE }, * ] * clock: 0.05s * } */ public class Slidding extends CustomNode { public attribute content: Node[]; public attribute clock = 0.1s; public attribute width: Number; public attribute autostart = true; public attribute cycle = true; public attribute anim = Timeline { repeatCount: Timeline.INDEFINITE keyFrames : [ KeyFrame { time : clock action: function() { for(node in content){ node.translateX--; if (node.getX() + node.translateX + node.getWidth() <= 0){ if(cycle){ node.translateX = width - node.getX(); } else { delete node from content; } } } } // action } // keyframe ] } // timeline public function create(): Node { // if width is not setted, we try to figure out if(width == 0) { for(node in content) { if(node.getX() + node.getWidth() > width) { width = node.getX() + node.getWidth(); } } } // normaly the slidding will start automaticaly if(autostart){ anim.start(); } // just a Group of Nodes return Group { content: content }; } } |
Is not the final implementation but it’s a idea. Soon I’ll show a demo game I did using theses codes.
This post continues a serie of posts I’m writing about 2D game development in Java.
A simple example of an JPanel that implements KeyListener (and a little trick) to handle KeyEvents to move a white square.
import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import javax.swing.JPanel; import javax.swing.JTextField; public class KeyPanel extends JPanel implements KeyListener{ private int x=50,y=50; public KeyPanel() { JTextField textfield = new JTextField(); textfield.addKeyListener(this); add(textfield); textfield.setPreferredSize(new Dimension(0,0)); } public void keyTyped(KeyEvent e) {} public void keyReleased(KeyEvent e) {} public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_LEFT) x-=5; if (e.getKeyCode() == KeyEvent.VK_RIGHT) x+=5; if (e.getKeyCode() == KeyEvent.VK_DOWN) y+=5; if (e.getKeyCode() == KeyEvent.VK_UP) y-=5; this.repaint(); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); g.setColor(Color.black); g.fillRect(0, 0, 500, 500); g.setColor(Color.white); g.fillRect(x, y, 50, 50); } } |
Download the complete NetBeans source project files: KeyTest.tar.bz2.
Tilesets are a common technique in game development to create all kinds of tile-based games (from strategy to RPG games).
Here’s a example of simple 2D isometric square tilesets. I decided to use 32×32 pixels tiles and store 10 tiles per row in a single image:
I created a class called public class JGameCanvas that extends from JPanel from swing:
package game; import java.awt.Color; import java.awt.Graphics; import java.awt.Image; import java.awt.Toolkit; import javax.swing.JPanel; enum Tile { GRASS, GRASS_STONE, GRASS_BAGS, T3, T4, T5, T6, T7, T8, T9, TREE, TREE_CHOMP, TREE_DEAD, T13, T14, T15, T16, T17, T18, T19, ROAD_H, ROAD_V, ROAD_HV_DOWN, ROAD_HV_UP, ROAD_VH_RIGHT, ROAD_VH_LEFT, ROAD_CROSS, T27, T28, T29, WALL, WALL_POSTER, WALL_END_RIGHT, WALL_END_LEFT, T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44, T45, T46, T47, T48, T49, NEWS, T51, RES_1, RES_2, BUSS_1, BUSS_2, HOSP_1, HOSP_2, MARK_1, MARK_2, PIZZ_1, PIZZ_2, RES_3, RES_4, BUSS_3, BUSS_4, HOSP_3, HOSP_4, MARK_3, MARK_4, PIZZ_3, PIZZ_4, RES_5, RES_6, BUSS_5, BUSS_6, HOSP_5, HOSP_6, MARK_5, MARK_6 } public class JGameCanvas extends JPanel{ private static final int tW = 32; // tile width private static final int tH = 32; // tile height private static final Tile map[][] = {{Tile.TREE,Tile.TREE, Tile.TREE, Tile.ROAD_V, Tile.GRASS, Tile.TREE, Tile.TREE_DEAD, Tile.GRASS_STONE, Tile.TREE, Tile.TREE}, {Tile.WALL, Tile.WALL_POSTER, Tile.WALL_END_RIGHT , Tile.ROAD_V, Tile.WALL_END_LEFT, Tile.WALL, Tile.WALL_END_RIGHT, Tile.TREE_CHOMP, Tile.GRASS_STONE, Tile.GRASS_STONE}, {Tile.GRASS,Tile.GRASS, Tile.GRASS_STONE, Tile.ROAD_V, Tile.GRASS, Tile.GRASS, Tile.GRASS, Tile.GRASS, Tile.GRASS, Tile.GRASS}, {Tile.PIZZ_1,Tile.PIZZ_2, Tile.GRASS, Tile.ROAD_V, Tile.GRASS, Tile.GRASS, Tile.GRASS, Tile.GRASS, Tile.GRASS, Tile.GRASS}, {Tile.PIZZ_3,Tile.PIZZ_4, Tile.GRASS, Tile.ROAD_V, Tile.GRASS, Tile.GRASS, Tile.MARK_1, Tile.MARK_2, Tile.HOSP_1, Tile.HOSP_2}, {Tile.ROAD_H,Tile.ROAD_H, Tile.ROAD_H, Tile.ROAD_VH_LEFT, Tile.TREE, Tile.TREE_DEAD, Tile.MARK_3, Tile.MARK_4, Tile.HOSP_3, Tile.HOSP_4}, {Tile.GRASS,Tile.BUSS_1, Tile.BUSS_2, Tile.ROAD_V, Tile.TREE, Tile.NEWS, Tile.MARK_5, Tile.MARK_6, Tile.HOSP_5, Tile.HOSP_6}, {Tile.GRASS,Tile.BUSS_3, Tile.BUSS_4, Tile.ROAD_VH_RIGHT, Tile.ROAD_H, Tile.ROAD_H, Tile.ROAD_H, Tile.ROAD_H, Tile.ROAD_H, Tile.ROAD_H}, {Tile.GRASS,Tile.BUSS_5, Tile.BUSS_6, Tile.ROAD_V, Tile.GRASS, Tile.GRASS, Tile.GRASS, Tile.GRASS, Tile.GRASS, Tile.GRASS}, {Tile.GRASS,Tile.GRASS, Tile.GRASS, Tile.ROAD_V, Tile.GRASS, Tile.GRASS, Tile.GRASS, Tile.GRASS, Tile.GRASS, Tile.GRASS} }; private Image tileset; public JGameCanvas() { tileset = Toolkit.getDefaultToolkit().getImage(this.getClass().getResource("resources/tileset.png")); } @Override protected void paintComponent(Graphics g) { g.setColor(Color.black); g.fillRect(0, 0, getWidth(), getHeight()); for(int i=0;i<10;i++) for(int j=0;j<10;j++) drawTile(g, map[j][i], i*tW,j*tH); } protected void drawTile(Graphics g, Tile t, int x, int y){ // map Tile from the tileset int mx = t.ordinal()%10; int my = t.ordinal()/10; g.drawImage(tileset, x, y, x+tW, y+tH, mx*tW, my*tH, mx*tW+tW, my*tH+tH, this); } } |
Program running:
Those graphics I created for the game Batalhão and are under Creative Commons Attribution Share Alike 3.0 license. The source code is under GPL license, download the NetBeans project with sources: tileset.tar.bz2.
An side-scrolling game attempt.
I used two images, this mountain background made with Gimp (xcf sources here) and that ship above made with Inkscape (svg sources here).
import javafx.ui.*; import javafx.ui.canvas.*; var scroll; scroll = [1..800] dur 60000 linear continue if true; var mountains = Clip{ transform: bind translate(-scroll,0) shape: Rect {x:bind scroll, y:0, width:400, height:200} content: [ImageView { transform: translate(0,0) image: Image { url: "http://silveiraneto.net/downloads/mountains.png"} }, ImageView { transform: translate(800,0) image: Image { url: "http://silveiraneto.net/downloads/mountains.png"} } ] }; var h = 50; var ship = ImageView { cursor: HAND transform: bind translate(0,h) image: Image { url: "http://silveiraneto.net/downloads/jfx_plane.png"} onMouseDragged: operation(e) { h += e.localDragTranslation.y; } }; Canvas { content: [mountains, ship] } |