I got the itch to design a game after toying around with Google's App Engine framework. I wrote a slideshow unwrapper for my wife to use when browsing on Ebay or Etsy, which lets you paste in several URLs with slideshows in them, and builds a single page containing all the images. App Engine's "fetch url" function and the ability to execute arbitrary Java code made that a snap. While learning the App Engine ropes, I found that it can serve an applet, and receive an arbitrary serialized object and store it in Google's "data store".
In other words, you can play a game in an applet, and save the game state to the data store.
I don't think that can be extended to be an MMORPG, since keeping ports open for always-on two way communication isn't supported, but something approaching a MUD wouldn't be too far fetched.
As it turns out, Java objects extend perfectly into defining RPG concepts. Stats (strength, dex, hit points, etc.) can be an object. "Item" can be an abstract class, where Armor, Weapon, and Scroll can be concrete extensions of Item. "Action" can be abstract, with Script, Combat, and Retail as concrete extensions. Fun stuff. Here is a screenshot of an early experiment with combat where a level 3 player with a sword and shield goes to battle with a level 1 skeleton armed, sadly, with only a bone:
For displaying the player on a world map, I want to leave him centered while the world moves around him. Right now this is all simple icon-based with no animation (the first couple screenshots below show only solid color icons, the last is using the icon set from Ultima 4), but that can be extended if I ever get the game mechanics fleshed out and complete. Knowing Jack about animation, that should be fun if I ever get there.
The scrolling turned out to be an interesting puzzle. I have an image representing the world map, and superimposed on that is the player icon, it whatever position he happens to be in the world. Both of those, in turn, are placed on a larger image that is only a black background. When the larger "scene" image is displayed, the player's position is always in the center of the display pane. Here are a couple screenshots showing how that is going to work (you're the red guy):
...and the Java code that makes it happen. If you're a budding game developer, feel free to snag and extend:
package cea.applet; import java.awt.Color; import java.awt.Graphics; import java.awt.Image; import java.awt.Point; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.image.BufferedImage; import javax.swing.JApplet; import javax.swing.JPanel; public class LApplet extends JApplet implements KeyListener { // Image array representing "cells" on the board Image[][] icons; // Image representing the absolute board Image board; // The player's icon Image player; // The board, plus the player at his position, plus a black background Image scene; // Where the player currently is. To cut down on multiplication, this is the absolute position // on the board image to display the player. Point playerPos; // How many cells per side are on our square board private static final int boardIconsPerSide = 26; // How many pixels per side are on our square icons private static final int iconSide = 24; // Number of pixels on the board's side private final int boardSide = boardIconsPerSide * iconSide; // Where the upper and left corner of the board can be found on the scene image private final int offset = -boardSide / 2; @Override public void init() { // Three solid-color icons representing different types of terrain Image green = getIcon(Color.GREEN); Image blue = getIcon(Color.BLUE); Image grey = getIcon(Color.GRAY); // Basic red icon representing the player player = getIcon(Color.RED); // Begin the player on the board's center square int startPos = (boardIconsPerSide / 2) * iconSide; playerPos = new Point(startPos, startPos); // Enumerate the icons at each point on the board. The positions for each color here are arbitrary icons = new Image[boardIconsPerSide][boardIconsPerSide]; for (int y = 0; y < boardIconsPerSide; y++) { for (int x = 0; x < boardIconsPerSide; x++) { if (y < 3 && x < 3) icons[x][y] = blue; else if (y > 6 && x > 6) icons[x][y] = grey; else icons[x][y] = green; } } // Create the board image based on the above icon array setBoard(); // Set up the Applet's panel and keyboard listener JPanel p = new JPanel(); p.setBounds(0, 0, boardSide, boardSide); p.setBackground(Color.WHITE); p.setVisible(true); add(p); addKeyListener(this); } // Returns a solid-color image square public Image getIcon(Color c) { BufferedImage b = new BufferedImage(iconSide, iconSide, BufferedImage.TYPE_3BYTE_BGR); Graphics g = b.getGraphics(); g.setColor(c); g.fillRect(0, 0, iconSide, iconSide); g.dispose(); return b; } // Sets up the board image public void setBoard() { board = new BufferedImage(boardSide, boardSide, BufferedImage.TYPE_3BYTE_BGR); Graphics g = board.getGraphics(); for (int y = 0; y < boardIconsPerSide; y++) { for (int x = 0; x < boardIconsPerSide; x++) { g.drawImage(icons[x][y], x * iconSide, y * iconSide, null); } } setScene(); } // Draw the board, player at his position, and set against a black background to allow us to // scroll and still leave the player in the center of the display panel public void setScene() { if (scene == null) { int sideX3 = boardSide * 3; scene = new BufferedImage(sideX3, sideX3, BufferedImage.TYPE_3BYTE_BGR); Graphics g = scene.getGraphics(); g.setColor(Color.BLACK); g.fillRect(0, 0, sideX3, sideX3); g.dispose(); } Graphics g = scene.getGraphics(); g.translate(boardSide, boardSide); g.drawImage(board, 0, 0, null); g.drawImage(player, playerPos.x, playerPos.y, null); g.dispose(); repaint(); } @Override public void paint(Graphics g) { // Explicitly set the paint area to the size of the board to prevent overflow when the player moves g.setClip(0, 0, boardSide, boardSide); // Draw the scene so that the player is centered in the display panel g.drawImage(scene, offset - playerPos.x, offset - playerPos.y, null); } // Let the player move his avatar with arrow keys public void keyPressed(KeyEvent e) { switch(e.getKeyCode()) { case KeyEvent.VK_LEFT: if (playerPos.x > 0) playerPos.x-= iconSide; break; case KeyEvent.VK_RIGHT: if (playerPos.x < (boardSide - iconSide)) playerPos.x+= iconSide; break; case KeyEvent.VK_UP: if (playerPos.y > 0) playerPos.y-= iconSide; break; case KeyEvent.VK_DOWN: if (playerPos.y < (boardSide - iconSide)) playerPos.y+= iconSide; } setScene(); } public void keyReleased(KeyEvent e) { } public void keyTyped(KeyEvent e) { } }
Finally, here is a later build showing actual icons in place of solid colors. A little corny, but it illustrates that graphics can be easily swapped and extended, leaving gameplay mechanics untouched.
No comments:
Post a Comment