Tile Game (Pokemon Wannabe)
A minimal but functional tile-based 2d game the likes of the good ol' Pokemon Gen 2. This was my first real big accomplishment in my video game learning journey. I was inspired by Pokemon Crystal, which was the game that really introduced me into video games.
This is a minimal but solid implementation. That is only basic two directional movement as well as collision (quad/tile collision) is supported. There is also 2d animation, inventory management, camera scrolling, world bounds clipping, texture loading, input handling and more. I quit further progress, when I was about to add a skeleton as an enemy and implement a fight system (since I wanted to take my knowledge in C++). However, the system is scalable so far and could be done with not much difficulty (if you still program in AWT that is).
It is pretty low-level; as such proper software engineering concepts are taught such as Singleton, Dependency Injection, State Design Pattern, GameLoop, Double Buffer Strategy, Runnable instance etc. It may serve as a basis for more complex projects, as it lays down fundamental concepts. I hope you can agree with me. :smile:
Used:
- Java 8 (no other libraries)
- the now mostly deprecated AWT (Abstract Window Toolkit). (Better use Swing now and Java 9+.)
- IntelliJ IDEA
- Windows
Controls
- W : move player up
- S : move player down
- D : move player right
- A : move player left
- I : toggle inventory
- LMB : attack (not finalized)
- RMB : attack (combat not completed)
Architecture Overview
TADAA!
Don't worry! We'll break it all down, piece by piece.
Launcher
class : Launches the Game singleton Instance. Optionally with window width & height as inputs.Game
class : The primary class. Runs off as a single thread (implements Runnable
). Encapsulates all game configuration and major functions.start()
: creates the single thread for the game to runstop()
: waits for the thread to bejoin()
edrun()
: runs the game loop which callsupdate()
andrender()
frame after frame (loop iteration) and sets up the frame timer to count the time that has elapsed since the previous iteration of the loop. This time is used forupdate
ing the game.init()
: initializes variables to default valuesupdate()
: updates the currentState
classrender()
: gets bufferStrategy from the buffer canvas (basically the “Render Target” for a window for DirectX connoisseurs) to draw into, gets the Graphics Context from it (which is basically our paintbrush we use to draw stuff) it clears the entire buffer and calls the current state'srender()
method to draw everything. Finally it displays the buffer and calls off drawing till next frame.- getters and setters
Handler
: dependency injection class. Created by theGame
. It gets passed around major game components in their constructor to initialize their variables.utility
: miscellaneous classesDisplay
: Manages painting and objects in the window/screenFrameTimer
: counts time elapsed per frameHandler
: encapsulates key Game information needed by various classes for initialization - Dependency Injection classUtils
: various utility functions. Here theloadFileAsString()
loads ourWorld
(eg level map)
State
: Implements theState
design pattern and switches between states. State is basically an abstract class that allows us to conveniently swap different behavior at runtime. This behavior is evaluated by a class that inherits fromState
. Thus we can callState::udpate
andState::render
without needing to know what state we're on and given miscellaneous game properties we get the appropriate (for example if we're paused we use theMenuState()
update()
andrender()
which is different behavior from theGameState()
's functions of the same name). There are 2 statesGameState
: delegates to world.update()MenuState
Here's how the game looks when on the MenuState
and the player's current items:
World
: a game level or map (currently there's only 1 level consisting of 256 tiles). It doesloadWorld()
given file path which is basically a 2d array of "tiles”. It hasupdate()
andrender()
methods which in return callupdate
andrender
on the manager classes.Tile
: the base representation for a tile, withupdate
andrender
methods. Lots of classes inherit from it depending on what type of tiles you want to have, egGrassTile
,RoadTile
,WaterTile
,TreeTile
etc.Item
: similar to Tile. Additionally there are utilities for creating new items and destroying them.Entity
: similar toTile
. However entities are considered the live objects in the world, eg thePlayer
,Creature
s etc. In other words, they are rendered above otherTile
s (higher priority) and they can move along the world. Entities implement gameplay properties and can interact with other entities or with the world. Entities also have collision boxes (hitboxes) and wecheckEntityCollisions()
. Most of them have health and other common attributes. Health can be decreased viahurt(int amount)
method. The most important entity is thePlayer
.Player
: (which inherits fromEntity
). You can see the hitbox bounds in the picture below. Notice that it does not overlap the player as that wouldn't be too realistic. Imagine that a character can duck or huddle to decrease its volume. Thus this smaller hitbox approximates this realism subtlety.Creature
: miscellaneous creatures or NPCs or enemies or friends or your mother in law.
ItemManager
contains anArrayList<Item>
of items toupdate
andrender()
. That's all just a collection ofItem
s.EntityManager
similar toItemManager
for Entities now. We can also compare entities for various purposes.Camera
: where we are in the world. In reality there is no camera. Depending on where we want to go, say we want to go left, the entireWorld
(and everything in it) is moved to the right by the same amount. In other words theCamera
is a reference point.centerOnEntity()
is called for the player (typically) and does exactly what it says. It centers the player in the window, UNLESS the player is along the bounds of the map which we determine by callingcheckForMapBounds()
.FontLoader
,ImageLoader
,SpriteLoader
(should have named itSpriteSheetLoader
- a collection of images cramped together in a single picture in a 2d array fashion) all load Fonts, Images, Sprites respectively. They delegate to java.awt library internal routines.Text
: Draws string to the window. Used primarily by the UI.UIObject
: abstract class, basically a tile/quadrilateral of “UI space” withonMouseMove
.UIObject
's need to implementupdate()
,render()
,onClick()
UIManager
: containsArrayList<UIObject>
to iterate over all of them nice and easyKeyManager
: inherits from java.awt.event.KeyListener.- Listens for keyboard events.
- Stores all possible keys (boolean array, true = key has been pressed etc.)
- Informs the game given a keypress. Does so via overriden methods
keyPressed()
,keyReleased()
,keyTyped()
,keyToggled()
MouseManager
: inherits from java.awt.event.MouseListener|MouseMotionListener. It's job is identical to theKeyManager
with the sole difference being that is manages mouse inputs.
Github
Github repository link.
Acknowledgements
I used the free font Morris Roman black by Dieter Steffmann.
Adobe Photoshop for the graphics and sprites (+ sprite sheets).