Saturday, October 5, 2013

Camera Scrolling

This is the second part of the TileMap in depth explanation.

A preface first: I want to say that in retrospect, I should have probably created a Camera class rather than rely on the TileMap to be the camera. The scrolling concept remains the same though.

I just remembered, I created an image explanation for scrolling.



The basic concept is that there are two different coordinates: the game coordinates, and the screen coordinates. Whenever I draw to the screen, I first have to translate everything over from game position to screen position.

So instead of drawing at say:

g.drawImage(image, x, y, null);

I draw at:

g.drawImage(image, x - cameraX, y - cameraY, null);  

If anyone's ever done OpenGL, it's similar to applying the (model)view matrix to transform game coordinates to camera space.

TileMap in depth

I didn't really explain how my TileMap class works, so I'll go into more detail here. If you want to follow along, I'm using the TileMap class from: https://dl.dropboxusercontent.com/u/59779278/Dragon%20Tale%20Tutorial%20P02.rar

First, I'll go over the fields.

// position
private double x;
private double y;

This is self-explanatory. These two variables indicate the top left corner of the TileMap. As the player moves to the right, the TileMap moves to the left, ie: x decreases.

// bounds
private int xmin;
private int ymin;
private int xmax;
private int ymax; 

These are the minimum and maximum bounds of the map. The position (x, y) cannot go further than these limits. This prevents the "camera" from scrolling past the edges of the TileMap.

private double tween;

This is a factor between 0 and 1 that indicates the speed in which the TileMap's position changes. A tween of 1 means that the TileMap will move to a given position instantly, where as a tween of 0.1 means that the TileMap will move 10% towards a given position. This gives a smoother scrolling effect.

// map
rivate int map[][];
private int tileSize;
private int numRows;
private int numCols;
private int width;
private int height;

The map is simply a 2d array of integers, where each integer represents a tile from the tileset. The tileSize is the size of a single tile in pixels. numRows and numCols are the number of rows and columns of the map. width and height are the dimensions of the map in pixels.

// tileset
private BufferedImage tileset;
private int numTilesAcross;
private Tile[][] tiles;

The tileset is the entire tileset image. From that, I read each individual tile subimage, and store it in the tiles array. The numTilesAcross is used to calculate the "id number" of the tiles.






// drawing
private int rowOffset;
private int colOffset;
private int numRowsToDraw;
private int numColsToDraw;

These are for drawing. They indicate the row and column that drawing should start, and how many rows and columns to draw. This is so that only part of the TileMap on the screen gets drawn, rather than the entire TileMap. (With respect to drawing, these variables aren't important because the Graphics object has a device clip, which prevents any drawing from happening outside of the Graphics' device's visible region. But from a complexity point of view, it does reduce the time from O(n^2).)



 The constructor is simple. It sets the tileSize, the tween, and the number of rows and columns to draw.

The loadTiles(String s) method reads in the tileset and cuts out each individual tile to put into the tiles array. My TileMap class isn't very scalar, in that it has a specific way of handling tilesets. The tileset image must always have 2 rows of tiles. The top row are all the non-blocked tiles, and the bottom row are all the blocked tiles.

 The loadMap(String s) methods fills in all the field variables under // map. The map file contains the number of rows and columns of the map, and the map itself, a 2d array of integers. I get the numRows, numCols, width, height, and bounds of the map.

Then there are a few getters and setters. Simple.

Next is the setPosition(double x, double y) method. This method tries to move the TileMap's (x, y) position to the given destination (x, y). This is where tween kicks in to smoothly move the TileMap to the destination. Then I make sure the position doesn't go past the bounds using fixBounds() and then calculate the row and column offsets to know where to start drawing.

The last thing is the draw(Graphics2D g) method. This is a straight forward 2d array loop. I loop through the map array starting with the rowOffset and colOffset, up to the specified numRowsToDraw and numColsToDraw. Now remember the map array contains integers of tileset "id numbers." I have to calculate, say, the id number 24 into row 2 column 4 (see tileset image above.) Since I know the numTilesAcross of the tileset, I can easily translate the id number using:

 int idNumber = map[row][col];
int translatedRow = idNumber / numTilesAcross;
int translatedCol = idNumber % numTilesAcross;

Then I just draw the given tile at the correct position and that's it. 

Hopefully, this answers some questions about the TileMap. I'll make a separate post about the "camera" scrolling.

Friday, September 27, 2013

State Based AI

I want to explain a little bit about FSM based AI. The concept is simple: an AI has a set of behaviors, and acts on a behavior depending on certain conditions. The enemies in Cranky Rampage all use this method.

The Sentry is your basic cannon fodder enemy. It can be in any of three states:

private int state;
private final int MOVING_TO_PLAYER = 0;
private final int SHOOTING = 1;
private final int COOLDOWN = 2;

When in the MOVING_TO_PLAYER state, the Sentry will simply try to get closer to the player. Once it reaches a certain proximity, it will move on to the SHOOTING state. Here, it will stop moving and fire a bullet. Then it will move into the COOLDOWN state where it will wait for some time. I have to find the edges in this FSM, meaning I have to find which states can transition to which other states.

MOVING_TO_PLAYER transitions to SHOOTING, SHOOTING transitions to COOLDOWN, and COOLDOWN can either go back to MOVING_TO_PLAYER or SHOOTING. So here's our list of edges for the graph:

MOVING_TO_PLAYER --> SHOOTING : if within range
SHOOTING --> COOLDOWN
COOLDOWN --> MOVING_TO_PLAYER : if out of range
COOLDOWN --> SHOOTING : if within range

Now coding the Sentry's AI is simple.

if(state == MOVING_TO_PLAYER) {
    if(player.getx() < x) {
        left = true;
        right = false;
    }
    else if(player.getx() > x) {
        left = false;
        right = true;
    }
    if(Math.abs(player.getx() - x) < range) {
        state == SHOOTING;
    }
}
else if(state == SHOOTING) {
    left = right = false;
    GameObjectFactory.createBullet(...);
    state = COOLDOWN;
}
else if(state == COOLDOWN) {
    timer++;
    if(timer > time) {
        timer = 0;
        if(Math.abs(player.getx() - x) < range) {
            state = SHOOTING;
        }
        else {
            state = MOVING_TO_PLAYER;
        }
    }
}

This is an easy way to give your NPCs some behavior. State based AI is simple to implement and can be effective.


Sunday, September 22, 2013

Cranky Rampage

I seriously never would have thought my channel would get so many subscribers. As of this post, I'm sitting at 865. I remember back when I posted a few videos about setting up a game with only a few video views and a couple subs. I was pretty content with that. It's just been crazy now.

Unfortunately I don't have any more tutorial ideas. So this is probably the end of that run. The only thing left is to just make some games.

I recently got started on a new game: Cranky Rampage. It's about a guy who just wants to get some sleep, but the alien invasion is making too much noise, so he decides to do something about it. This game is a throwback to arcade platform shoot em ups.


I'm hoping to get a demo + source released soon.

Lol nope. I've stopped working on it. Here's the source. You can dissect it if you want.
https://dl.dropboxusercontent.com/u/59779278/games/Cranky%20Rampage.rar

Saturday, August 31, 2013

Pulse

Been a while. I'm just finishing up the last few levels of a new game I'm working on, "Pulse." It's a simple game that can get challenging. The goal is to get the ball to the target by pushing it along with the mouse. You have a limited number of pushes available and any extra pushes you have when the player reaches the target adds to the score for that level. There are eight levels, each revolving around a theme.





I'll update with the finished game some time soon.

Game:
https://dl.dropboxusercontent.com/u/59779278/games/Pulse.jar

Eclipse Source Code:
https://dl.dropboxusercontent.com/u/59779278/games/Pulse.rar

Friday, June 21, 2013

Ludum Dare test

I'm going to try to enter my first Ludum Dare. It's a 48 hour game development competition. Obviously, that is an incredibly short time frame to create a game from scratch. So in order to prepare, I've decided to test what I can actually accomplish in two days.

Diamond Hunter is what I came up with. It's a very simple puzzle game where you must collect all diamonds to win. You'll also need to find certain items to get past some obstacles.

I kept it simple so code problems were not an issue, graphics not too ambitious, and music is a looped chord progression that I hope doesn't get too tiring to listen to. Overall, I think it turned out great. I'd definitely like to do another test run.

Language: Java 7 (Java 2D)
IDE: Eclipse
Graphics: Photoshop
Sound: Magical 8Bit Plug

Game:
https://dl.dropboxusercontent.com/u/59779278/Diamond%20Hunter.jar

Eclipse Project:
https://dl.dropboxusercontent.com/u/59779278/Diamond%20Hunter.rar

Sunday, June 16, 2013

First test

This is not the whole game, but I really wanted to put something out there. Consider this a demo.

Game:
https://dl.dropboxusercontent.com/u/59779278/Artifact.jar

Eclipse Project:
https://dl.dropboxusercontent.com/u/59779278/Artifact.rar

Gameplay video:
http://www.youtube.com/watch?v=5pyv5qOAofI

Thursday, May 16, 2013

So far I've converted the engine to use frame based timing. I know a lot of people will think this isn't the correct way to handle it. However I'm running on an extremely slow laptop with an integrated chip and it's pumping out 60 FPS consistently. The only real problem is if someone actually has a slower laptop than me--highly unlikely. It's a 2D game with no raster editing and no complicated physics. For the purposes of this game, frame based timing is more than adequate.

I've implemented an event system. This makes it easy to create cutscenes when certain requirements are met. In this scene, the gem explodes, the portal opens, and a bunch of... things... fly out.


The prologue is almost finished.

After this comes the real work: the levels. With these levels comes new tilesets, new enemies, new bosses, new music, new everything. Time to put on my seat belt.

Tuesday, May 14, 2013

Well, well, well. Things are looking up. I'm getting a lot more finished than I've originally anticipated. I started on a new tile set and a prototype map.



Monday, May 13, 2013

I wouldn't call myself an artist, but I do draw on occasion. However drawing pixel sprites is a whole 'nother beast.

Man oh man, I can spend a single day working and only manage to get one sprite action finished.
 It's coming along though. Slowly.


Saturday, May 11, 2013

Working


I've been working through this game that I wanted to use as a tutorial for game structuring. The series has gotten to the point where the main game engine is pretty much finished. You have the tools--all that's left is to add more enemies, more levels, more music, more graphics, more content. I won't be adding another video to the series until I've finished the game created enough to make it look like an actual game. I'm working on a major overhaul right now.

Updates with game development to follow.