To start off, the reason I'm creating this journal is because this will be my first "complete" game I've made. Years ago I made a shell of a survival game in XNA and that was the closest I ever got to a finished project. My goal for this post and future posts is to hopefully share some knowledge about LibGDX and get feedback about terrible my programming practices :P
Lets jump right into it!
The premise of this game is pretty simple. You play as some dude whose image is based off of a character in one of my favorite books. +10 to anyone who can guess which book.
There really isn't a story yet, although the goal of the game is to journey through 7 dungeons, or the 7 layers of hell, defeat the bosses, and then kill satan possibly? I haven't thought of an ending yet. When you first start off in the town area, youll notice all the NPCs are ghosts and you can't interact with them, the exception being a single priest in the giant tower. As you finish each dungeon, more and more NPCs return to their original form.
Although this sounds fairly linear, I'm trying to move away from it by incorporating a "purgatory" system when the player dies. Instead of a game over, the player is sent to purgatory where he has the option to descend the layers of hell with the current skills available, or return to the surface via rising from a grave to acquire better skills and potions.
Weapon and armor equips won't be a part of the game at this point. I have much love for the way the Witcher 3 handles their potion system, so I'm using inspiration from that to make the player stronger. As the player defeats monsters, items will be picked up. At the end of the day you have an option to make camp and create and consume up to 2 or 3 potions which buff you for the whole day afterwards.
Obviously a lot of this hasn't been implemented yet, but it's nice to have goals to work on.
The title of the post says two months worth of development, it may be more like 4 or 5. It's only been 2 months since I've put my project onto Github. I was wanting to start a journal earlier, but the artwork was so terrible that I felt too ashamed to share it. Look at these awesome slimes!
So far my biggest struggle is moving forward has been the artwork. I am by no means a good artist, and to make things worse I chose to use pixel style art. But I've limited myself to a maximum of 2 hours a day working on the art so I don't get frustrated and give up. The world feels bland and boring without trees and bushes, so wandering around at this point is useless.
The map at this point right now is huge, maybe too huge. You can see the areas I've outlined as different biomes and areas for the dungeons. My greatest challenge I feel will be to make this world feel alive and vibrant.
Now on to code things.
I've used LibGDX before for smaller projects like your basic pong and breakout clones, but this is the first time I've delved so far into the library. Right now I have something like 15 enemies planned for the world, and I use a class called EnemyFactory for creation. public Enemy createEnemy(EnemyType type, Vector2 position){ Enemy enemy = null; String id; switch (type){ case SLIME: id = "slime" + idNumber; enemy = new Enemy(position, id); enemy.isEnemy = true; enemy.sprite = new Sprite(new Texture("slime.png")); enemy.createBody(position, enemy.sprite.getTexture()); enemy.health = 10; enemy.enemyType = EnemyType.SLIME; enemy.itemToDrop = Item.SLIME_GOO; enemy.attackCount = 0; enemy.attackTimer = 0.2f;
Eventually this will be put into a json or xml file, but I don't know anything about those languages yet so this works for now. Enemies are spawned using tiled map objects like so:for(MapObject object : tiledMap.getLayers().get("SpawnLayer").getObjects().getByType(RectangleMapObject.class)){ Rectangle rect = ((RectangleMapObject)object).getRectangle(); if(object.getProperties().containsKey("slime")){ mapEntities.add(MainGame.enemyFactory.createEnemy(Enemy.EnemyType.SLIME, new Vector2(rect.getX() / MainGame.PPM, rect.getY() / MainGame.PPM))); }
This was one of my biggest reasons for using LibGDX. Using tiled map data has made things like teleporting to different maps and spawning enemies so much easier. Another reason for using this library is Box2D. I hate math and hard-coding values for collisions, so Box2D was a godsend for what I needed. In LibGDX you have a class called ContactListener which handles all collisions in your world. public void beginContact(Contact contact) { Fixture fixtureA = contact.getFixtureA(); Fixture fixtureB = contact.getFixtureB(); Player player; Entity entity; Projectile projectile; if(fixtureA == null || fixtureB == null)return; if(fixtureA.getBody().getUserData() == null || fixtureB.getBody().getUserData() == null)return; if(isProjectileHitEnemy(fixtureA, fixtureB)){ entity = fixtureB.getBody().getUserData() instanceof Entity ? (Enemy)fixtureB.getBody().getUserData() : (Enemy)fixtureA.getBody().getUserData(); projectile = fixtureA.getBody().getUserData().equals(Statics.PLAYER_PROJECTILE) ? (Projectile)fixtureA.getUserData() : (Projectile)fixtureB.getUserData(); subject.notify(projectile, entity, Event.ENEMY_DAMAGE);// projectile.isActive = false; System.out.println("PROJECTILE HIT ENEMY"); }
This is nice because it allows you to use the user data from a Box2D body to see what is hitting what.
A day/night cycle has been implemented as well. Box2DLights is another great option that LibGDX offers. With dynamic hard and soft shadows, you really can't go wrong with it. public void updateLight(DayNightCycle dayNightCycle, Camera camera){ if(dayNightCycle.getHours() == 6){ ambientB = 0.3f; ambientR = 0.8f; ambientG = 0.35f; } if(dayNightCycle.getHours() == 7){ ambientG = 0.65f; ambientR = 0.9f; ambientB = 0.01f; } if(dayNightCycle.getHours() == 8){ ambientB = 1; ambientG = 1; ambientR = 1; } if(dayNightCycle.getHours() == 17){ ambientR = 1; ambientG = 0.8f; ambientB = 0; } if(dayNightCycle.getHours() == 19){ ambientR = 1; ambientG = 0.5f; ambientB = 0; } if(dayNightCycle.getHours() == 20){ ambientR = 1; ambientB = 0.3f; ambientG = 0.3f; } if(dayNightCycle.getHours() == 21){ ambientG = 0.2f; ambientB = 0.7f; ambientR = 0.3f; } if(dayNightCycle.getHours() == 22){ ambientR = 0.2f; ambientG = 0.15f; ambientB = 0.4f; ambientA = 0.3f; } rayHandler.setAmbientLight(ambientR, ambientG, ambientB, ambientA); playerLight.setPosition(MainGame.player.sprite.getX(), MainGame.player.sprite.getY()); rayHandler.setCombinedMatrix(camera.combined); rayHandler.updateAndRender(); }
Holy if statements! Maybe one day I'll figure out how to make a smooth transition from daylight to darkness, but it is not this day. There really isn't anything more to Box2DLights than the initialization of the light and the update method! How great is that?
Here are a couple of gifs of the lighting and combat system in action:
I'll try to make more frequent posts as I implement more of my ideas into this project, and hopefully with better artwork so there's something nice to look at. Feel free to give advice or ask questions! I feel like a have a decent understanding of LibGDX at this point and I highly recommend it to anyone who wants to make a game in Java :)