Rogues' Redemption - roguelike with pixel graphics

There are various ways to implement field of view functionality in a roguelike. Initially I attempted to project from each map tile back to the player unhiding the tile if the ray makes it back to the player without colliding with a solid tile. Although this kind of worked, it was problematic both technically and visually. There were a lot of artefacts and the code to sort this out was complicated and not eligant.

So I re-wrote the code to mimic a radar of sorts. Rays are projected out from the player and each tile that is reached is then unhidden. Once the ray hits a solid tile it stops.

Technically, this is a simple approach to the problem but provides good and consistent results. Initially, the radar did a full 360 degree sweep in one frame but this was very slow on Android so it’s now chopped into 16 slices; this still achieves nearly 4 full sweeps per second. I quite like the way this suggests the hero is quickly looking around the room rather than the whole room suddenly popping into view when the door is opened.

The gif is slower than the actual radar in the game.

3 Likes

I’m surprised that it was slow. When you say that you did a 360 sweep, in what increments did you trace lines out from the player’s position? Do you have a limited field of view or can the player see an infinite distance?

It scans in increments of 0.025 radians, which is about 1.5 degrees at a time and scans out to the edge of the screen at most. It does this in 16 slices. Here’s the code if anyone is interested - this is by no means the best solution to the problem but it does work reliably:

local data = require ("main.data")

function init(self)
	self.scan = 0
	self.levelurl = string.format("level%d:/level#tilemap", data.level)
end

function update(self, dt)
	local p2 = vmath.vector3(0,0,0)

	for a = self.scan, self.scan + (math.pi/8), 0.025  do
		
		p2.x = math.sin(a) + data.playerpos.x
		p2.y = math.cos(a) + data.playerpos.y
		
		lineofsite(self, data.playerpos, p2)
	end
	
	self.scan = self.scan + (math.pi/8)
end

function lineofsite(self, p1, p2)
	local v = p2 - p1
	local len = math.sqrt((v.x * v.x) + (v.y * v.y))
	
	v.x = (v.x / len) * 4
	v.y = (v.y / len) * 4
		
	local p = p1 + v
	local c = false
	local s = 0
	
	while c == false and s < 22 do
		p.x = p.x + v.x
		p.y = p.y + v.y

		local t = data.gettile(self, p.x, p.y, "world")
		if t >= 280 then
			c = true
		end

		data.settile(self, p.x, p.y, "fog", 0)

		s = s + 1
	end
	
	--msg.post("@render:", "draw_line", { start_point = p1, end_point = p, color = vmath.vector4(1, 1, 1, 1) } )
end

Some things that come to mind:

  1. Nitpicking but what the hell: lineofsight, not lineofsite
  2. When iterating along a line I’d look into using Breshenhams line drawing algorithm. There’s no math.sqrt() involved and it’s generally considered fast.
  3. If you do a lot of calls to functions that are set on tables (such as math.sin) it’s a good idea to cache those functions in local variables. This way there’s no table lookup every time you need to call the function. Check page #3 of this Lua Performance Gems: https://www.lua.org/gems/sample.pdf. You need to be calling the function a lot for it to really be measurable, but in games performance is important, and who knows, maybe you need to start calculating field of view for many agents every frame and then it really matters.
  4. You can probably skip many rays if you hit something that blocks line of sight near to the player. In that case it’s very likely that many of the subsequent rays being cast will pass through the same blocking ray and in that case it’s pointless to even cast those rays in the first place.
3 Likes

As @britzl mentions, a Bresenham algorithm would probably be suitable for this. (link found here)

Also, I came to think of maybe adapting a floodfill of sorts, and again, googled it, and found that someone (ofc) already had had the same idea: Dijkstra + line of sight. Ofc, as is, the result is a bit “blocky”, but I kind of like that in a rogue like.

Now, I think I found my programming exercise for the day :slight_smile:

4 Likes

Lots of new stuff in this update! I’ve been busy on many aspects of Rogue’s Redemption.

First of all, I’ve decide to target Android for this particular game. I’m writing it really as a game I’d like to play myself on the go. Somehow I find most mobile games disappointing in one way or another so would like to make something that’s actually good fun to play on a mobile device.

The interface has been developed a bit more with the addition of health and xp bars. Also a profile icon has been added in the top right of the screen. This will be used for various things, the specifics of which have yet to be decided.

The player can actually die now! The game feels more like a real game now that’s possible. Combat is still arbitrary with fixed amounts of damage being done on each attack. I’m not familiar with D&D and don’t particularly want to be so all the ‘chance’ combat events will be kept minimal and will behave more as you’d expect. One thing I don’t like about some roguelikes is when the random and luck elements seem to often dictate the outcome of a game.

30+ of the planned 60+ usable items in the game now spawn from chests, boxes are as drops from dead monsters. All the code to actually use the various items has yet to be written.

There are a few sound effects now for various events and actions. This really helps build the atmosphere of the game. These will obviously be added to and some suitable music added too.

The line of sight code has been re-written twice now! The latest incarnation uses Bresenham’s line-drawing algorithm as suggested by some of the guys here. It’s currently much slower than my previous vector based code but it can be optimised greatly. Right now it checks every pixel of the line and really only needs to check about 1 in 4 or even 1 in 8.

There is now a ‘ticker’ which saves 4 lines of text displaying the last 4 notable events to happen. Most roguelikes seem to have this and I like it so there we go.

The greatest challenge at the moment is collision detection. Defold’s collision shape implementation isn’t well suited to a grid-based game like a roguelike. I really need a way of just checking a tile for an enemy and act accordingly. With the loosely-coupled nature of Defold, I’m honestly not sure how to do this. I’m sure a solution will present itself soon enough - it always has done thus far!

Thanks for reading. Here’s a screenshot from the latest build:

7 Likes

Looks really nice! Can’t wait to play it! :slight_smile:

1 Like

I agree, it looks great!

Do you really have to use Defold’s collision detection for a grid and turn based game such as a roguelike? I would create a full representation of the level in a Lua table structure and do FOV, pathfinding and all of that stuff on that structure and only use the game objects as a visual representation of that main data structure.

1 Like

Lots of “under the hood” development has been happening this week as well as some more visible additions.

Part of the roguelike mix is inventory management - I’ve further developed the inventory code which now allows the player to pick up/drop and use or equip various items. There are a few more bits to finish off this part of the game and that should be done in the next few days.

I’ve re-used some code from Psychon 2 - that is, the mini map code. Some re-arranging was needed as now the map is slowly unfolding before you and the minimap reflects this - in Psychon 2 the map was generated at the beginning of the level but now it needs to continually be generated because it keeps changing.

The interface and osd has also had some further tweaks and is (I hope) near to being finalised. I’ve been spending more time testing the game on an actual phone to make sure it works nice and smoothly - so far so good :slight_smile:

Monsters now have little health bars over there heads.

Functionality for the first trap to go in the game is in and working - a very simple trap that looks like a mushroom and explodes poisonous spores everywhere when you go near it. Monsters can also trigger traps so it can be quite fun tricking them into blundering into one :grin:

Still lots to do - more items to add, quests to take on and all the balancing / progression through the levels to sort out.

As always, thanks for reading and here are a few screen-shots.

8 Likes

Cool, it looks great! How many equipment slots will you have? Main hand+off hand, chest, helmet, boots, ring, amulet?

1 Like

Thanks! And yes, there are 6 equip-able slots - main hand, secondary hand, armour, helmet, amulet and ring. There’s some stuff in the wrong slots in the screenshot above but it’s been fixed now!

Should look something more like this:

6 Likes

They say rewrites are the bane of all roguelikes - thus far, I’ve known this to be 100% true. This week I’ve rewritten most of the collision system, parts of the spawning systems and various routines that deal with the various coordinate systems used in the game (screen, map and world). The code is ‘feeling’ more cohesive and stable now and I’m confident the game can now be more easily expanded.

Ranged Attacks

Having completed work on melee attacks near the beginning of development, it was time to get the ranged attacks working. So far, the fireball attack is the only available ranged attack but the code can easily be reused for any number of other ranged attacked. Having these available to the player increases the strategy element of the game significantly.

Mana

The player now has mana points (MP) to use with various spells. The aforementioned fireball is currently the only functioning spell but the mana system is ready to be used for any number of other spells in the future. Some of the supporting stuff such as the ability to drink mana potions is also done.

Balance

I’ve started to think more about balancing the game and tidied up a few things. I’ve implemented a system to prevent stuff spawning in the wrong places or on top of other stuff. The types of monster now get steadily harder as you progress through the levels e.g. you’ve only vampire bats and goblins to deal with on level 1 but around level 15 you’ll be tackling trolls and dragons.

Progression

There is now a complete game cycle - a basic front end leads you into the game and you can progress through levels building up your character before eventually dying and returning the the front end to start again. Dying happens a lot in roguelikes!

Various other areas of the game continue to be gradually added to and improved. This following week will mostly be taken up with adding functionality for weapons, spells and other items. I may even start thinking about quests.

6 Likes

Just one month ago, I began developing Rogue’s Redemption. Although it’s been hard work, I’m amazed at how much has been achieved in such a short space of time. I continue to find Defold very rewarding and quick to use.

This week, I’ve spent most of my time balancing various elements of the game and adding items and effects. The various collectable items now show up in a more orderly fashion as the player progresses through the game - so you wont be seeing the best weapon in the game popping up on level 2 any more. This will need more tweaking and testing at a later date once some other stuff has been finalised.

I’ve also added various scrolls, potions and spells. The particlefx feature of Defold has been great fun (and quick) to use and try out various ideas for special effects. The game continues to run fast on Windows and both my Android devices.

Also the usual fixes, tweaks and minor changes you’d expect during development of any game.

The first NPC is also present in the game though he has no real functionality at this point. He’ll be a mobile shop of sorts and the player will be able to buy and sell items with him when he pops up every few levels.

There are a couple of traps working now - the poison trap and the sticky trap. I’ll be adding more as the ideas present themselves.

For the following week I’ll be working more on NPCs, quest givers etc.

Some screens of various spells and scroll effects:-

8 Likes

Looks great!

2 Likes

Early HTML5 Build

I’m sharing an early build of Rogue’s Redemption. It gives a good general idea of what to expect of the game and how it currently all fits together. The game will eventually be released on Android.

The map function doesn’t work in the HTML5 build at the moment - I don’t know why yet

Helpful comments appreciated!

Play HTML5 build in browser

6 Likes

Cool! It plays really well I think. I would like to have keyboard input when playing on desktop/browser, but I understand that you want to do a mobile game. One thing I’d like to see is some path finding where I can click on a tile and then the game would pathfind and move me one step at a time towards that position, as long as the position has been explored before. Also, the pathfinding should stop when an enemy is visible.

4 Likes

This week I’ve been working on some new graphics and have started to look at character classes. There will be 4 to choose from (probably) and each class will have various strengths and weaknesses as you’d expect.

A lot of tidying up has also been done. I thought this was a good point to make sure everything is neat before continuing.

I’ve also started the shop/trading screen. At the moment the player can only buy stuff though I may put in selling too at a later date.

The game now saves your progress when the game loses focus and the main menu offers the option to load that saved game and continue or start a new one.

This coming week I’ll continue working on the shop, NPC interaction, add more graphical improvements and sound effects.

3 Likes

I’m getting well into the latter half of development on Rogues Redemption. Most features and systems are in place and working - some still need polish and tweaking.

New Stuff

  • Character selection screen allows the player to choose from 3 different classes of character
  • The shop now works when clicking on the trader NPC who can be found loitering in the corridors of some levels
  • Player stats screen displays various bits of information about the player, some useful, some less so
  • The in-game menu toggles music, sound effects etc. and gives the option to return to the main menu
  • There are now special locked rooms which contain special objects and items
  • Player now gets hungry and will require a regular intake of food

Lots of fixes, graphical tweaks, new sound effects, balancing and re-writing of code as I discover better ways of doing things.

5 Likes

Oh, hunger? Hmm, so by what means can the player find food? I’m somewhat sceptical of food as a way to force the player to progress and explore further. It can work, but there has to be multiple options. Dungeon Crawl Stone Soup implements food in a good way with multiple ways to get satiated.

1 Like

Food is plentiful throughout the dungeon - there are boxes to be broken open that contain food. I wouldn’t normally bother with such a thing but it does serve a purpose. As the player’s mana regenerates slowly over time, the hunger element deters players from waiting out many turns until the mana fills up and them going on a rampage with a fireball spell or something. The hunger is simply there to prevent players “cheesing it” to get further through the game.

2 Likes