Changelog:
- PNG alpha channel instead of white background.
- 70% alpha channel for shadows.
- Added a television that I forgot to put since first version.
Changelog:
Changelog since the last version:
I’m trying to find a good style for outside building for this set. I tried some kinds of roofs but they are not good yet.
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.
Although everything I do is under the license Creative Commons Attribution Share-Alike, I’m starting to group my pixelarts works and release them in single files under the GPL.
Here the first and small release:
If you are looking for a free, big and great tileset, look the wonderfull work of The Mana Word project (both code and art under GPL).
Soon I’ll group others pixel art drawings of others games I made in a single tileset file.
If you have a similar work in a compatible license we can make release of them togheter and create a big free tileset.
A simple plain char in a “pokemonic” style I’m doing to use with My Free TileSet. These will be base to create another free and open characters for games.
Animated:
And here a lot of them in home:
Under Creative Commons Attribution-Share Alike license.
JavaFX 1.0 is out and there are tons of new cool features, specially for game development.trans
I’ll show in this tutorial how to create a very simple demo that shows how to load imtrages, handle sprites, collisions and keyboard events that you can use to create a game with a old school rpg like vision.
For the background scenario I’m using the house that I drew and we’ll call as house.png.
That we load as a Image and place into a ImageView.
ImageView{ image: Image {url: "{__DIR__}house.png"} } |
For the character I’m using the last character I drew, the nerdy guy.
To make the animation easier, I spited it into 9 pieces:
down0.png, down1.png and down2.png
left0.png, left1.png and left2.png
right0.png, right1.png and righ2.png
up0.png, up1.png and up2.png
All images I’m using should be in the same directory of source code.
Let’s start loading the scenario and a single character sprite.
import javafx.stage.Stage; import javafx.scene.Scene; import javafx.scene.image.*; Stage { title: "RPG-like demo", width: 424, height: 412 visible: true scene: Scene{ content: [ ImageView{ image: Image {url: "{__DIR__}house.png"} }, ImageView{ x: 320 y: 80 image: Image {url: "{__DIR__}down1.png"} } ] } } |
Saved as Game.fx you can compile and run with in your terminal:
$ javafxc Game.fx
$ javafx Game
Hint: You can use NetBeans 6.5 JavaFX plugin to easier the JavaFX development.
To put animation on the character we load all sprites into four lists. Each list for each direction.
// sprites def up = for(i in [0..2]) { Image {url: "{__DIR__}up{i}.png" } } def right = for(i in [0..2]) { Image {url: "{__DIR__}right{i}.png" } } def down = for(i in [0..2]) { Image {url: "{__DIR__}down{i}.png" } } def left = for(i in [0..2]) { Image {url: "{__DIR__}left{i}.png" } } |
And create vars to store the character position and frame of animation.
var frame = 0; var posx = 320; var posy = 80; |
Also store the house background.
// house background def house = ImageView{ image: Image {url: "{__DIR__}house.png"} };
I create booleans to store some key states and at each interval of time I see how they are and do something about. You can handle keyboard event with less code but I like this way because keep visual and game logics a little bit more separated.
// keyboard var upkey = false; var rightkey = false; var downkey = false; var leftkey = false; // player var player = ImageView{ x: bind posx y: bind posy image: Image {url: "{__DIR__}down1.png"} onKeyPressed: function(e:KeyEvent){ if (e.code == KeyCode.VK_DOWN) { downkey = true; } else if (e.code == KeyCode.VK_UP) { upkey = true; }else if (e.code == KeyCode.VK_LEFT) { leftkey = true; }else if (e.code == KeyCode.VK_RIGHT) { rightkey = true; } } // onKeyPressed onKeyReleased: function(e: KeyEvent){ if (e.code == KeyCode.VK_DOWN) { downkey = false; } else if (e.code == KeyCode.VK_UP) { upkey = false; }else if (e.code == KeyCode.VK_LEFT) { leftkey = false; }else if (e.code == KeyCode.VK_RIGHT) { rightkey = false; } } // onKeyReleased } |
See a video of the game working so far:
Now we will add collisions. In a previous post I showed some math behind bounding box game collisions. The good news are that you no longer need to worry about that. There are a lot of API improvements in JavaFX 1.0 that do all the hard work for you, specially the new classes on javafx.geometry package, Rectangle2D and Point2D.
We create rectangles that represent the obstacles in the house.
// collidable obstacles def obstacles = [ Rectangle { x: 0 y: 0 width: 32 height: 382 stroke: Color.RED }, Rectangle { x: 0 y: 0 width: 414 height: 64 stroke: Color.RED }, Rectangle { x: 384 y: 0 width: 32 height: 382 stroke: Color.RED }, Rectangle { x: 0 y: 192 width: 128 height: 64 stroke: Color.RED }, Rectangle { x: 192 y: 192 width: 64 height: 64 stroke: Color.RED }, Rectangle { x: 224 y: 0 width: 32 height: 288 stroke: Color.RED }, Rectangle { x: 288 y: 128 width: 96 height: 64 stroke: Color.RED }, Rectangle { x: 0 y: 352 width: 128 height: 32 stroke: Color.RED }, Rectangle { x: 192 y: 352 width: 192 height: 32 stroke: Color.RED }, Rectangle { x: 224 y: 320 width: 32 height: 32 stroke: Color.RED }, Rectangle { x: 32 y: 64 width: 32 height: 32 stroke: Color.YELLOW }, Rectangle { x: 64 y: 64 width: 32 height: 32 stroke: Color.YELLOW }, Rectangle { x: 96 y: 64 width: 32 height: 32 stroke: Color.YELLOW }, Rectangle { x: 128 y: 64 width: 64 height: 32 stroke: Color.YELLOW }, Rectangle { x: 192 y: 32 width: 32 height: 32 stroke: Color.YELLOW }, Rectangle { x: 64 y: 128 width: 64 height: 32 stroke: Color.YELLOW }, Rectangle { x: 32 y: 250 width: 32 height: 32 stroke: Color.YELLOW }, Rectangle { x: 64 y: 250 width: 64 height: 32 stroke: Color.YELLOW }, Rectangle { x: 200 y: 255 width: 20 height: 20 stroke: Color.YELLOW }, Rectangle { x: 200 y: 170 width: 20 height: 20 stroke: Color.YELLOW }, Rectangle { x: 257 y: 32 width: 32 height: 32 stroke: Color.YELLOW }, Rectangle { x: 288 y: 32 width: 32 height: 32 stroke: Color.YELLOW }, Rectangle { x: 320 y: 192 width: 64 height: 64 stroke: Color.YELLOW }, Rectangle { x: 352 y: 295 width: 32 height: 60 stroke: Color.YELLOW }, Rectangle { x: 32 y: 327 width: 64 height: 23 stroke: Color.YELLOW }, ]; |
We just have to change a little bit the game logics in order to handle collisions.
We define a bounding box around the player, it’s a rectangle from (4, 25) at the player coordinates system and with width 19 and height 10. The idea is to prospect where the player will be in the next step, see if it’s bouding box don’t collide with any obstacle and so pass it to the real game position.
// game logics var gamelogics = Timeline { repeatCount: Timeline.INDEFINITE keyFrames: KeyFrame { time : 1s/8 action: function() { var nextposx = posx; var nextposy = posy; if(downkey) { nextposy += 5; player.image = down[++frame mod 3]; } if(upkey) { nextposy -= 5; player.image = up[++frame mod 3]; } if(rightkey) { nextposx += 5; player.image = right[++frame mod 3]; } if(leftkey) { nextposx -= 5; player.image = left[++frame mod 3]; } for(obst in obstacles) { if(obst.boundsInLocal.intersects(nextposx + 4, nextposy + 25, 19, 10)) { return; } } posx = nextposx; posy = nextposy; } } } |
This is enough to do the trick but I also added a way to smoothly show the obstacles when pressing the space key.
Here is the complete source code.
package Game; import javafx.stage.Stage; import javafx.scene.*; import javafx.scene.image.*; import javafx.scene.input.*; import javafx.scene.paint.*; import javafx.scene.shape.*; import javafx.animation.*; var frame = 0; var posx = 320; var posy = 80; // sprites def up = for(i in [0..2]) { Image {url: "{__DIR__}up{i}.png" } } def right = for(i in [0..2]) { Image {url: "{__DIR__}right{i}.png" } } def down = for(i in [0..2]) { Image {url: "{__DIR__}down{i}.png" } } def left = for(i in [0..2]) { Image {url: "{__DIR__}left{i}.png" } } // house background def house = ImageView{ image: Image {url: "{__DIR__}house.png"} }; // keyboard var upkey = false; var rightkey = false; var downkey = false; var leftkey = false; // player var player = ImageView{ x: bind posx y: bind posy image: down[1] onKeyPressed: function(e:KeyEvent){ if (e.code == KeyCode.VK_DOWN) { downkey = true; } else if (e.code == KeyCode.VK_UP) { upkey = true; }else if (e.code == KeyCode.VK_LEFT) { leftkey = true; }else if (e.code == KeyCode.VK_RIGHT) { rightkey = true; } if(e.code == KeyCode.VK_SPACE){ if(fade==0.0){ fadein.playFromStart(); } if(fade==1.0){ fadeout.playFromStart(); } } } // onKeyPressed onKeyReleased: function(e: KeyEvent){ if (e.code == KeyCode.VK_DOWN) { downkey = false; } else if (e.code == KeyCode.VK_UP) { upkey = false; }else if (e.code == KeyCode.VK_LEFT) { leftkey = false; }else if (e.code == KeyCode.VK_RIGHT) { rightkey = false; } } // onKeyReleased } // collidable obstacles def obstacles = [ Rectangle { x: 0 y: 0 width: 32 height: 382 stroke: Color.RED }, Rectangle { x: 0 y: 0 width: 414 height: 64 stroke: Color.RED }, Rectangle { x: 384 y: 0 width: 32 height: 382 stroke: Color.RED }, Rectangle { x: 0 y: 192 width: 128 height: 64 stroke: Color.RED }, Rectangle { x: 192 y: 192 width: 64 height: 64 stroke: Color.RED }, Rectangle { x: 224 y: 0 width: 32 height: 288 stroke: Color.RED }, Rectangle { x: 288 y: 128 width: 96 height: 64 stroke: Color.RED }, Rectangle { x: 0 y: 352 width: 128 height: 32 stroke: Color.RED }, Rectangle { x: 192 y: 352 width: 192 height: 32 stroke: Color.RED }, Rectangle { x: 224 y: 320 width: 32 height: 32 stroke: Color.RED }, Rectangle { x: 32 y: 64 width: 32 height: 32 stroke: Color.YELLOW }, Rectangle { x: 64 y: 64 width: 32 height: 32 stroke: Color.YELLOW }, Rectangle { x: 96 y: 64 width: 32 height: 32 stroke: Color.YELLOW }, Rectangle { x: 128 y: 64 width: 64 height: 32 stroke: Color.YELLOW }, Rectangle { x: 192 y: 32 width: 32 height: 32 stroke: Color.YELLOW }, Rectangle { x: 64 y: 128 width: 64 height: 32 stroke: Color.YELLOW }, Rectangle { x: 32 y: 250 width: 32 height: 32 stroke: Color.YELLOW }, Rectangle { x: 64 y: 250 width: 64 height: 32 stroke: Color.YELLOW }, Rectangle { x: 200 y: 255 width: 20 height: 20 stroke: Color.YELLOW }, Rectangle { x: 200 y: 170 width: 20 height: 20 stroke: Color.YELLOW }, Rectangle { x: 257 y: 32 width: 32 height: 32 stroke: Color.YELLOW }, Rectangle { x: 288 y: 32 width: 32 height: 32 stroke: Color.YELLOW }, Rectangle { x: 320 y: 192 width: 64 height: 64 stroke: Color.YELLOW }, Rectangle { x: 352 y: 295 width: 32 height: 60 stroke: Color.YELLOW }, Rectangle { x: 32 y: 327 width: 64 height: 23 stroke: Color.YELLOW }, ]; // game logics var gamelogics = Timeline { repeatCount: Timeline.INDEFINITE keyFrames: KeyFrame { time : 1s/8 action: function() { var nextposx = posx; var nextposy = posy; if(downkey) { nextposy += 5; player.image = down[++frame mod 3]; } if(upkey) { nextposy -= 5; player.image = up[++frame mod 3]; } if(rightkey) { nextposx += 5; player.image = right[++frame mod 3]; } if(leftkey) { nextposx -= 5; player.image = left[++frame mod 3]; } for(obst in obstacles) { if(obst.boundsInLocal.intersects(nextposx + 4, nextposy + 25, 19, 10)) { return; } } posx = nextposx; posy = nextposy; } } } gamelogics.play(); // obstacles view var fade = 0.0; var obstacleslayer = Group { opacity: bind fade content: [ Rectangle { x:0 y:0 width:500 height: 500 fill: Color.BLACK }, obstacles, Rectangle { x: bind posx + 4 y: bind posy + 25 width: 19 height: 10 fill: Color.LIME } ] } var fadein = Timeline { keyFrames: [ at (0s) {fade => 0.0} at (1s) {fade => 1.0} ] } var fadeout = Timeline { keyFrames: [ at (0s) {fade => 1.0} at (1s) {fade => 0.0} ] } // game stage Stage { title: "RPG-like demo", width: 424, height: 412 visible: true scene: Scene{ fill: Color.BLACK content: [house, player, obstacleslayer] } } |
Play Through Java Web Start
or click here to play via applet, inside your browser.
update: The applet version and Java Web Start versions should be working now. The applet version on Linux seems to be having problems with the keyboard handling, use the Java Web Start version while I’m trying to fix it.
Downloads:
Can't find what you're looking for? Try refining your search: