[Slick2d] Retro-Pixel Castles > Now on Steam! <

Better still, a BinaryHeap. (Source in RotT originally courtesy of pjt33 - still coming in useful to this day!)

Cas :slight_smile:

I’ll give both a shot and see what happens. :slight_smile:

Any little tiny microsecond of performance I can squeeze out of my pathing is critical, since I have monsters and villagers all over the map (hundreds at a time sometimes) wandering all over the place constantly, and due to game mechanic design/requirements I can’t ā€œput them to sleepā€ in inactive areas like most games would. :slight_smile:

@Rayvolution I don’t know if it would be applicable, but have you looked into faster pathing algorithms like JPS?


http://zerowidth.com/2013/05/05/jump-point-search-explained.html

yeah, I looked into JPS quite a lot a few weeks ago, the problem is my maps don’t actually have any ā€œBlockedā€ tiles, making JPS not quite work the way it’s intended since it’s designed to find blocked areas quickly and eliminate bad paths while shooting in the directions of good ones. (and thus, also removing a ton of open tiles by quickly filling large areas on the map). I’m not sure what’ll do in my situation, I think it may actually slow down my performance by opening tons of high-cost nodes very quickly (That would be blocked in a normal map/game) that might not of been touched at all with regular A*, as my A* implementation is fairly good at making straight lines out of those large rooms.

The big problem in my game is that without blocked tiles all mountains and trees end up open nodes instead of closed off/blocked nodes, since they have a terrain cost they need to be part of the Queue, and if I use JPS it may more frequently fill up the queue with those tiles (and leaving them open, slowing down the overall loop).

It’s a complicated problem, because those tiles need to stay open so they can slowly work there way through (in the same way you would figure the cost of moving through tar/mud), so that if pathing fails without going through the mountains or trees it’ll already be on it’s way to build the shortest path that may involve hacking away at the topography to force it’s way to the target.

But, with my current implementation of binarySearch() (or with PriorityQueue/BinaryHeap if Riven and Cas’s suggestions work out) I don’t think the actual size of the node’s collection/list will actually impact performance nearly as much as I expected it to when I first looked into JPS. I now sort the ArrayList in descending order with binarySearch() and a custom comparator (lowest on bottom) so the extremely high cost nodes are mostly untouched when a new node is inserted since it’ll only push the (usually minimal or non existent) values lower than it to the right. JPS might help out greatly since it won’t matter nearly as much how large the ArrayList is anymore.

So, tl;dr, I might give it a shot again and see what happens. :smiley:

Hmm, sounds tricky. But yeah, if you’re not using a heap/priorityqueue for A*, then there is almost certainly room for improvement right there.

I might slap the tail from this thread on the RPC game thread later. DONE :yawn:

Having said that, binary search is cache trashing galore. Given your access patterns, it might be faster to do a linear search from the tail of the array, or a binary search on the last X elements, if that fails, a binary search on the range before it. In the end memory bandwidth (and cache trashing) is probably your bottleneck, making these quirky, hackish tricks worthwhile.

Everybody knows binary search scales O(log n), but given the characteristics of modern CPUs, it at one point takes a nose dive, once the working set doesn’t fit into cache anymore, and nearly every memory access is a cache miss.

Now, ocourse, this all is meaningless without your current performance numbers, your data models and the characteristics of your maps… which means we’re mostly making bad assumptions about whatever it is your dealing with.

On that note, if you find out PriorityQueue is a poor fit, and BinaryHeaps seem tempting… keep in mind d-ary heaps (which are like binary heaps, but have d children, where d is a constant, >= 2) which are theoretically slower, but due to better memory management (less cache misses) outperform BinaryHeaps in almost every case… or so they say.

Have you consided mipmapping your graph?

Generally the most effective optimisation strategy is discarding the majority of your data :persecutioncomplex:

… or not doing any optimisation at all, if it’s actually fast enough and there’s other things to be doing instead :wink:

Cas :slight_smile:

Probably wise, we’re way off topic. :slight_smile:

yeah, without everyone fulling understanding the system it’s hard to offer advice, and it’s definitely not a standard system. I think PriorityQueue may be the way to go, since it natively functions the same way I’ve Jerry-rigged my current ArrayList. :slight_smile:

Yep, I’ve tried to trim out as much as humanly possible, and only (pre)calculated what was absolutely necessary. :slight_smile:

Normally I’d agree, but in this case pathfinding is probably a stress spot in my entire program. :smiley:

@ Riven - PriorityQueue took a full complex path across the entire map from 55-57ms down to 10-11ms. Awesome! :slight_smile:

I think this is where I take Cas’ advice and consider it fast enough to get the job done. :stuck_out_tongue_winking_eye:

Today, I actually had a good sit down this evening and played Retro Pixel Castles for the first time :slight_smile: Can’t figure out how to make food! Gah.
While I was playing I had the good sense to profile it, and here is what it produced:


Flat profile of 2278.94 secs (172965 total ticks): main

  Interpreted + native   Method
  0.3%     0  +   258    org.lwjgl.opengl.WindowsDisplay.nGetPbufferCapabilities
  0.0%     0  +    41    org.lwjgl.opengl.WindowsContextImplementation.nSwapBuffers
  0.0%     0  +    32    org.lwjgl.opengl.WindowsDisplay.nResetDisplayMode
  0.0%     0  +    31    java.io.FileOutputStream.close0
  0.0%     0  +    30    org.lwjgl.opengl.WindowsDisplay.nSwitchDisplayMode
  0.0%     0  +    26    org.lwjgl.opengl.GL11.nglGetFloatv
  0.0%     0  +    25    java.net.SocketInputStream.socketRead0
  0.0%     0  +    21    java.net.DualStackPlainSocketImpl.connect0
  0.0%     0  +    21    org.lwjgl.opengl.EXTFramebufferObject.nglCheckFramebufferStatusEXT
  0.0%     0  +    18    sun.nio.ch.FileDispatcherImpl.close0
  0.0%     0  +    14    org.lwjgl.opengl.WindowsDisplay.nDestroyWindow
  0.0%     0  +     9    org.lwjgl.opengl.GL11.nglGetTexImage
  0.0%     1  +     8    org.lwjgl.opengl.GL11.nglGenTextures
  0.0%     0  +     9    org.lwjgl.opengl.WindowsPeerInfo.nChoosePixelFormat
  0.0%     0  +     8    org.lwjgl.opengl.GL11.nglTexImage2D
  0.0%     0  +     7    java.io.RandomAccessFile.close0
  0.0%     0  +     7    java.io.WinNTFileSystem.rename0
  0.0%     0  +     7    org.lwjgl.openal.AL10.nalBufferData
  0.0%     0  +     5    sun.nio.fs.WindowsNativeDispatcher.CreateFile0
  0.0%     0  +     5    java.io.WinNTFileSystem.delete0
  0.0%     0  +     5    org.lwjgl.opengl.WindowsContextImplementation.nDestroy
  0.0%     0  +     4    java.util.zip.ZipFile.close
  0.0%     0  +     4    java.net.NetworkInterface.getAll
  0.0%     0  +     3    java.io.WinNTFileSystem.getLastModifiedTime
  0.0%     0  +     3    java.io.FileInputStream.readBytes
  0.8%    75  +   638    Total interpreted (including elided)

     Compiled + native   Method
  0.4%   375  +     0    rpc.particles.ParticleModule.update
  0.1%   118  +     2    rpc.entities.EntityModule.getMobType
  0.1%    55  +     0    org.newdawn.slick.state.StateBasedGame.update
  0.1%    53  +     0    rpc.utilities.PathFinder.searchForResourceTile
  0.1%    51  +     0    rpc.entities.EntityBase.updateAnimation
  0.1%    43  +     5    rpc.guielements.play.Interface.render
  0.0%    43  +     0    rpc.particles.ParticleModule.render
  0.0%    36  +     2    rpc.utilities.PathFinder.findPath
  0.0%    37  +     0    rpc.entities.EntityBase.combatSearch
  0.0%    32  +     2    rpc.particles.ParticleModule.initController
  0.0%    25  +     0    rpc.entities.EntityReachMap.buildReachMap
  0.0%    23  +     2    rpc.resources.ResourceModule.updateResourceCount
  0.0%    20  +     0    org.newdawn.slick.openal.WaveData.convertAudioBytes
  0.0%    18  +     1    rpc.entities.EntityBase.updateAI
  0.0%    18  +     0    rpc.map.CollisionModule.initController
  0.0%    18  +     0    rpc.objects.ObjectRangeBuilder.createSingleRange
  0.0%    16  +     0    rpc.BackgroundModule.update
  0.0%    13  +     0    rpc.resources.ResourceModule.renderSelection
  0.0%     1  +    10    rpc.LightingModule.addLightStraight
  0.0%     9  +     1    rpc.ShadowModule.initController
  0.0%     7  +     2    rpc.LightingModule.updateLightMap
  0.0%     8  +     0    rpc.utilities.PathFinder.searchForEntity
  0.0%     7  +     0    rpc.entities.EntityModule.update
  0.0%     6  +     0    rpc.entities.essence.EssenceBase.checkTarget
  0.0%     5  +     0    rpc.entities.VillagerUtility.getHarvestingWork
  1.3%  1119  +    62    Total compiled (including elided)

         Stub + native   Method
 56.6%     0  + 52790    org.lwjgl.opengl.GL11.nglGetFloatv
  9.0%     0  +  8399    org.lwjgl.opengl.WindowsContextImplementation.nSwapBuffers
  7.9%     0  +  7382    org.lwjgl.opengl.GL11.nglColor4f
  5.3%     0  +  4962    org.lwjgl.WindowsSysImplementation.nGetTime
  4.4%     0  +  4063    java.lang.Thread.yield
  3.9%     0  +  3677    org.lwjgl.opengl.GL11.nglTexCoord2f
  3.2%     0  +  2968    java.lang.Thread.sleep
  3.0%     0  +  2793    org.lwjgl.opengl.GL11.nglVertex3f
  1.0%     0  +   939    org.lwjgl.opengl.GL11.nglEnable
  0.8%     0  +   765    org.lwjgl.opengl.GL11.nglTranslatef
  0.6%     0  +   528    java.lang.Object.hashCode
  0.5%     0  +   434    org.lwjgl.opengl.GL11.nglBegin
  0.4%     0  +   375    org.lwjgl.opengl.GL11.nglBindTexture
  0.4%     0  +   365    org.lwjgl.opengl.GL11.nglEnd
  0.1%     0  +   106    org.lwjgl.opengl.GL11.nglGenTextures
  0.1%     0  +    68    java.util.zip.Inflater.inflateBytes
  0.1%     0  +    56    org.lwjgl.opengl.GL11.nglCallList
  0.1%     0  +    55    org.lwjgl.opengl.GL11.nglPopMatrix
  0.1%     0  +    51    java.io.FileInputStream.open0
  0.0%     0  +    41    org.lwjgl.opengl.GL11.nglVertex2f
  0.0%     0  +    39    org.lwjgl.openal.AL10.nalSourcef
  0.0%     0  +    38    org.lwjgl.opengl.GL11.nglLoadIdentity
  0.0%     0  +    36    org.lwjgl.opengl.GL11.nglGetError
  0.0%     0  +    34    org.lwjgl.opengl.GL11.nglPopClientAttrib
  0.0%     0  +    31    org.lwjgl.opengl.GL11.nglDisable
 98.0%     6  + 91398    Total stub (including elided)

  Thread-local ticks:
 46.1% 79665             Blocked (of total)
  0.0%     2             Class loader


Global summary of 2278.94 seconds:
100.0% 173043            Received ticks
  0.0%    75             Received GC ticks
  1.9%  3296             Compilation
  0.0%     2             Other VM operations
  0.0%     2             Class loader

Note here and there the total lack of any pathfinding code contributing to the CPU usage. In fact notice the total lack of CPU usage. You spend 98% of your time in native code! And half of it is nglGetFloatv, which I can’t for the life of me think why you would ever call it.

Cas :slight_smile:

The old pathfinding system is actually pretty solid, it can process 10-15 paths at once in about 15ms, so it’s even faster than the new system. It just doesn’t understand terrain movement costs (It’s similar to a Breadth First Search system). As for nglGetFloatv, I honestly have no idea. I don’t call it anywhere in my code, so I’m guessing it’s being called under the hood inside Slick2D somewhere. I’ll have to hunt it down, because that has me curious. :slight_smile:

Its percentage is probably inflated due to causing a driver stall. The OpenGL function is glGetFloat() BTW.

Line 300 and line 1745, in Graphics.java


	public Color getBackground() {
		predraw();
		FloatBuffer buffer = BufferUtils.createFloatBuffer(16);
@@		GL.glGetFloat(SGL.GL_COLOR_CLEAR_VALUE, buffer);
		postdraw();

		return new Color(buffer);
	}


	public void pushTransform() {
		predraw();
		
		FloatBuffer buffer;
		if (stackIndex >= stack.size()) {
			buffer = BufferUtils.createFloatBuffer(18);
			stack.add(buffer);
		} else {
			buffer = (FloatBuffer) stack.get(stackIndex);
		}
		
@@		GL.glGetFloat(SGL.GL_MODELVIEW_MATRIX, buffer);
		buffer.put(16, sx);
		buffer.put(17, sy);
		stackIndex++;
		
		postdraw();
	}

most peculiar :expressionless:

Yeeargh, Slick :point: Keviiiiiiiiiiiiin!

Cas :slight_smile:

A small grid might be 256x256=65536 cells. A navigation mesh of the same data might be some like a dozen…or maybe even dozens!

Greetings everyone,

For 7 days I will be giving out copies of Retro-Pixel Castles on Twitter. Winners will be announced at the end of each day! Already own a copy? Gift it to a friend!

To enter, simply follow @RaymondDoerr and retweet the current active tweet for that day. This post will be updated daily with latest active tweet, and a list of previous winners. Anyone can enter, names will be collected from the retweet list on twitter, thrown into a (figurative) hat and one name will be picked randomly!

Even if you win, you can even retweet the future active tweets to try your luck for a another copy!

Bonus: Winners Twitter accounts will be listed below, giving your account some free exposure! :slight_smile:

CURRENTLY ACTIVE: WEDNESDAY 15TH
Winner be announced early on the 16th just prior to the next tweet!

Winners:
TUESDAY, April 14th - Jon Hearn (@ZenDarklighter)

Upcoming tweets:
THURSDAY, April 16th.
FRIDAY, April 17th.
SATURDAY, April 18th.
SUNDAY, April 19th.
MONDAY, April 20th.

On the latest update ive noticed a few things , firstly the crystal motivator has no image in the menu and it has the same description as the cullus gate. Everything else is great.

I feel as if I need some sort of additions to the game wrt. attacking/defending. Like soldiers and barracks or something.

Cas :slight_smile:

That’s actually what I’m working on right now. :smiley:

I’m adding heroes to the game (Automated little villagers who can’t work, but defend the village), and the influence tab that will allow you to try to nudge your little guys to do things you want. But by design, it won’t always work.

Also going to be adding barracks and basic fighters too, although I’m not sure if they’ll make it into this next update.