- Registration/Login UI - support scrolling to input when keyboard opened - I broke this when switching to default ScrollPane.- Done
Progress today:
- Keyboard stays on screen after logging in.
- Login UI animation not running on desktop (actually not related to desktop)
- Login ui spacing does not reset after logging in and logging out.
- Logging out does not resume rotating animation.
- Login/Register UIs - work on desktop too (register ui)
- Email validation on backend
- Smoothing movement when walking around (instead of just jumping to the new position in a jerky fashion).
Working on an effect for when you click a building, so you see what you selected before the menu comes up. And uh…
Building selection is a little better now, with an effect that shows what you clicked before the UI takes over your screen:
Upcoming: Avatar creation UI, and rendering them above buildings, so you know which ones are yours and how to identify other people. Ability to actually attack will come soon after.
Also, deciding on how factions will work. Originally it was you join either The Dwell or the Uprising, but now I’m thinking that maybe The Dwell will just be the NPCs. So few options:
- The Dwell vs you & everyone else
- The Dwell vs you vs everyone else
- The Dwell vs Uprising
- The Dwell vs Uprising vs Rogue
Avatar system progress. I can now generate five different kinds of avatars (four more WIP), represented by a simple structure.
For example:
avatarDefinition.avatarType = AvatarType.WAVY;
avatarDefinition.parameters = new int[]{20, 20, 20};
avatarDefinition.colors = new int[]{Color.decode("#1f8483").getRGB(), Color.decode("#2bb9b7").getRGB(), Color.decode("#b94e2b").getRGB()};
Creates:
You’ll also be able to do some scale/rotating as well.
So I have avatars rendering above towers now, but realized since I’m using AWT to dynamically generate the avatars this won’t work! AWT is not available on Android. It looks like I’ll use LibGDX’s Pixmap class.
I do have to be able to render them on the client, since when you create the avatar you’ll want to make minor adjustments and have it update smoothly. Also, it saves bandwidth.
Avatar generation ported to be cross platform.
Merry Christmas everyone.
Almost have support for textured lasers.
Just one last issue. I need to get the quads to rotate to face the camera. Making them decals (like I do the avatars above the towers) doesn’t make sense, because I have to perform custom transformations to get the quads to start at one point and end at another.
float scaleX = 1;
float scaleY = target.position.dst(position);
float scaleZ = 1;
Vector3 up = new Vector3(target.position).sub(position).nor();
Vector3 forward = new Vector3(up).crs(Vector3.Z);
if (forward.isZero()) forward.set(Vector3.X);
forward.crs(up).nor();
Vector3 left = new Vector3(up).crs(forward).nor();
Vector3 direction = new Vector3(target.position).sub(position).nor();
Matrix4 transform = modelInstance.transform.cpy();
transform.setToTranslation(Vector3.Zero);
float[] val = transform.val;
val[Matrix4.M00] = left.x;
val[Matrix4.M01] = up.x;
val[Matrix4.M02] = forward.x;
val[Matrix4.M10] = left.y;
val[Matrix4.M11] = up.y;
val[Matrix4.M12] = forward.y;
val[Matrix4.M20] = left.z;
val[Matrix4.M21] = up.z;
val[Matrix4.M22] = forward.z;
transform.scale(scaleX, scaleY, scaleZ);
transform.setTranslation(new Vector3(direction).scl(scaleY / 2f).add(position.x, position.y, position.z));
modelInstance.transform = transform;
Yes I know, lots of allocations, working version comes before optimized version
This code was “inspired” by the arrow builder code
Anyone have any ideas as to how to get the quads to face the camera, while still transforming them to be stretched to given start/end points?
User avatars now render above creeps, and creep health is represented by their size. As lasers shoot the creeps, they diminish into nothing. Eventually the creeps will be represented as balls of energy.
Buildings with towers are now transparent instead of just having a wireframe.
v1 of laser system, although the laser quads still don’t rotate to face the camera
Here the same avatar is used for everything, but once the new avatar generation UI is done I will have a better demo.
Recently, the game has been unplayable on my $2k phone (2fps with creeps, 15 without) during stress testing. It’s kind of an extreme scenario, but it’s also a scenario I expect, for example:
Also, when entering an area with lots of towers, sometimes FPS would drop from 50 to 5-10 for a couple seconds…
Updates (so far) today:
- Removing copy-on-write data structures where I could.
- When adding towers to scene, don’t re-render duplicate segments. (bug)
- Lowering number of segments per tile from 100 to 64. This reduces the number of frustrum checks per tile, since we are not (currently) using a quad tree. This requires some fine tuning, as too large a segment and updating the game world will have noticeable lag, but too small and we waste CPU cycles checking if segments are visible. Likely I will be creating some kind of basic quad tree to solve this. We already have WorldTile->Segment, but likely we need a third layer.
- Experimenting with not re-rendering duplicate route segments. Until we can split routes across tiles or tile segments, this is actually slower, so disabled until that other work is done.
- Only make decals face camera when they, or the camera, changes rotation/position.
Result:
- Now at 7fps in stress test with creeps, 50 without.
Currently, roads are only divided up by world tile (a slippy tile at zoom level 15), while buildings are divided up into segments that are 1/8th the size of the same slippy tile.
We could potentially gain some FPS by splitting the road polygons into these segments, which we only render when they are in the frustrum.
Creep routes and the creeps themselves are just added directly to the world, they are not “sharded” up into different segments, which will be the next step. We’re rending way more than we need to! I expect this to raise the stress-test FPS from ~7 to 30. The goal is 60 on my fancy phone.
Update - splitting the creeps rendering up by segments (just hacked into building segment atm) gets us over 30FPS in our stress test! Yay!
More progress to be made. But a nice step.
So, to render the creeps, what I’m doing is:
modelBatch.render(modelInstance)
Is there a more efficient way for rendering something that is transformed every frame, in LibGDX?
EDIT: I noticed ModelBatch uses a RenderablePool without specifying the size, so it defaults to16. That means every frame I’m doing a few big allocations to grow the pool to a hundred/thousand in some cases.
Why isn’t it configurable? I’ll have to create my own ModelBatch class with a one line change…
EDIT2: I suppose another change is to batch the creeps up into a single mesh like I do with buildings, but this might get unwieldy, I’ll have to figure out an abstraction to handle this cleanly if there isn’t a better solution.
Yesterday I got to work on this some. Using a ModelCache for the creeps, and some other tuning, I can get to around 20-25FPS in the stress test on my phone.
While this seems like a drop, it also includes properly moving creeps between segments, logic which currently executes in the rendering loop until I can move it out, and find the right abstractions to deal with concurrent access to certain resources.
In order to represent the creeps as balls of energy, I think I’ll just go with decals and see how well decals work with animations, by changing the texture every frame.
EDIT: An alternative of course is a shader, but will try the easiest thing first.
This should get us closer to the final design, and should also perform much better.
Once we can get sustained 50-60 FPS during my stress test, I’ll know I have a solid foundation and can move on to other things.
Update:
- Moving creeps between segments is now off the UI thread
- Lasers now don’t all fire at once, there was a bug where the last attack time was not initially sent from the server. This reduces the occasional framerate drop. I’m still trying to improve the laser rendering. They’re just quads, but they have a lot of impact on the framerate for some reason. Using a model cache does not help.
- Various concurrency bugfixes and performance improvements
Still at around 20-25 FPS on my phone, but it’s more consistent now.
It caps out at 144 on my desktop.
Will work on it again Sunday. Today’s my birthday.
Update:
30-40 sustained FPS in our stress test!
Still not 60, but closer!
I realized there was no reason to calculate what is visible each frame. So now, I do that on a separate thread. Additionally, my render loop consisted of a tree structure, since the world is made up of tiles->quads->segments->renderables. We had to iterate through this tree of classes during each frame, probably causing lots of cache misses.
Instead now, that separate thread iterates the tree structure and populates an array of items to render. The render loop just polls from this list, meaning that rendering simply is just iterating through one array.
This is managed by having two buffers of renderables - one used for rendering, while the other is prepared for the next frame.
EDIT: 60FPS now!
I disabled blending on the avatars, which I don’t need, and also made the avatar resolution lower depending on the screen size.
The creep bodies can now be animations, here’s a test with some random GIF I found:
I have to figure out how to make nice plasma-like animations in Blender, then we’ll have our own And we can finally get around to making the tower attacks look nice too.
Today lots of progress on the synchronization between client/server:
- Instead of running a “similar” simulation on the client as the server (in regards to attacks), the server now sends the attacks that the client doesn’t yet know about, and the client just renders those. This improves the accuracy a lot and removes a lot of headaches for me that kept causing delays. If needed, we can always optimize it more - trying to duplicate the simulation is not worth the headache. This also saves a lot of CPU time / battery life on the client.
- Creep positions are still “simulated” separately however, but this is much easier than dealing with that and attacks.
- Fixed bug where eventually all the creeps on the screen would go away. This regression was introduced by our optimization to only render creeps on screen. Inside the CreepRenderable.render() method - we set a flag - “renderedLastPosition” to true. If this was false, we wouldn’t calculate the next position. This obviously no longer works with the new system - and without it the bug is fixed.
- A new debug system was built so that on the client I can see which creeps are being rendered that are no longer on the server, and also I can tell the client to inform the server about how many creeps it knows about. This way, on the server I can randomly select clients to see how accurate the synchronization is, if need be, to detect bugs.
Upcoming:
- Previously, creeps came from an object pool on the client. I removed that optimization while fixing the above. I need to add that back.
- Remove scrolling from the sign up flow - right now the sign up UI is terrible - can’t launch until that is fixed.
- Need to find someone to make animations for the creeps/lasers.
- Ability for creeps to attack towers!