Home Tags LÖVE und PANZER

LÖVE und PANZER - Smooth Real-time Networking

With the new networking structure in place, making everything look nice is important. In my last post, I explained the idea behind how our network code works. In this post, I will explain the details that made everything come together.

First off, a quick recap. We are sending data from the client to the server on a 20Hz frequency, whch means the client is sending data 20 times each second, or roughly once every three frames. The server grabs this data and relays it on to all other clients. Ignoring latency for a moment, that means that each client is receiving new position data every third frame. Between these updates, the other tanks will just sit on the map and then jump to their new position. This is really ugly. To solve that problem, we did two things:

1) Introduce velocity
2) Use velocity to move the tank in lieu of data

When the player pushes certain buttons on their gamepad or keyboard, (in this case, left-axis-y and W/S, respectively), those inputs are encoded as movement velocity and sent along with the current position so that the server and other clients know how fast you are moving. Coupled with rotational velocity (left-axis-x, A/D), others can know where you are, and where you will be in the next frame.

typedef struct {  
    uint16_t id;
    float turret;
    float turret_velocity;
    float position_x,     position_y,     position_z;
    float orientation_x,  orientation_y,  orientation_z;
    float velocity_x,     velocity_y,     velocity_z;
    float rot_velocity_x, rot_
Read More →

LÖVE und PANZER - Real-time Networking

I am well aware that this post is late. I was out of the province for a few weeks and hadn't the time to write. I rewrote the networking code several weeks ago and it works pretty well! Let me explain what I did.

So first off I want to explain my old networking code, and why it sucked. My first ever attempt at writing network code started a few years ago. What I ended up doing was serializing data every frame and sending it to the server. I serialized everything in a text-literal way, which is to say, I used a big ol' JSON string as my packet. Yeah. When I realize this was sending copious amounts of data over the network, I decided to try a "smarter" approach which was then copied over to LÖVE und PANZER. This approach was event-driven. Instead of sending data values every frame, I would send event flags such as "move=1" or "turn=-1" in a JSON string. On both server and client I would then interpolate moves based on which flags were active. This significantly reduced bandwidth but I was still sending JSON strings. Another hiccup was the server and client were never in parity. The server was authoritative so it was sending a force-sync command to the client every second or two with absolute data which would make objects on screen jump around a bit. After actually measuring the data in LÖVE und PANZER, I realized that I needed a whole new system that accounted for

Read More →

LÖVE und PANZER - I Broke Everything... Again

I have some good news, and some bad news. Which would you like to hear first? The bad news? Okay. The bad news is... we broke it. However, the good news is we added several new features and fixes, so let me explain those first!

First off, we felt it was important to add bounding boxes to all of our objects. Bounding boxes are very useful for things like collision, be it two players ramming into each other, or one player shooting another. We decided right away that it was important for each mesh to have its own bounding box, that way shooting the air between the cannon and the hull wouldn't cause a hit. The way we created the bounding box was quite straight forward: While loading the IQE model, we looped through each vertex and checked if that vertex had a larger or smaller x, y, or z position than the previous record holder. By the end of the loop, we knew the closest and farthest points of the mesh.

local bounds = { min = {}, max = {} }  
for i=1, #mesh.vp do  
    bounds.min.x = bounds.min.x and math.min(bounds.min.x, vp[1]) or vp[1]
    bounds.max.x = bounds.max.x and math.max(bounds.max.x, vp[1]) or vp[1]
    bounds.min.y = bounds.min.y and math.min(bounds.min.y, vp[2]) or vp[2]
    bounds.max.y = bounds.max.y and math.max(bounds.max.y, vp[2]) or vp[2]
    bounds.min.z = bounds.min.z
Read More →

LÖVE und PANZER - I Broke Everything

Rotated Turrets

Over the past few weeks, development has slowed down a bit while both Colby and myself had several other obligations to deal with. While development slowed, it did not stop entirely and we did manage to squeeze out several new features and fixes, both big and small.

After I added multiplayer support to our game, I decided to crack open Blender and rescale our tank model to be appropriately sized. I jumped on Wikipedia and looked up the size of the hull on a Panzerkampfwagen IV and scaled our tank up to match the length. This of course broke everything.

With the tank now a realistic size, the camera was in the wrong place, the terrain was too small, and the bilinear interpolation no longer worked smoothly. The quickest fix for the camera was to simply multiply the offset position by the tank scalar. While this worked, it didn't quite feel right any more so we spent a bit of time trying different values until we found a camera position that was comfortable. We plan to go back to that at a later date but for now, it's fine.

To fix the interpolation, we came up with a somewhat simple solution that we plan to refine in the coming weeks. The problem with the bilinear interpolation was that the tiles were so tiny compared to the resized tank that there was just too much jitter. The tank felt like it was falling over every little bump instead of smoothly riding over them. What we ended up

Read More →



In my previous post, I discussed how Colby and myself initially set up a 3D environment in LÖVE, aptly named LÖVE3D. In this post, I will be discussing some of the tools we're building for this environment, and challenges we've come across while building them.

After making a dance video to show off the abilities of my IQE loader, we decided to start work on a game. We had a simple panzer model kicking around, so we decided to make a simple tank shooting game. We started by modifying the world we already built by removing the dancing Mikus and replacing them with our tank. We attached the camera to the tank's body and allowed a togglable first-person view.

With a basic Player object, we quickly got bored of the flat surface we were driving on. We had two options: design a world in Blender and load it up, or procedurally generate a world on the fly. We decided the latter option would both be more interesting and less time consuming, so we researched noise generation algorithms. After looking at a few algorithms, we decided to use Simplex. Luckily, there was a Lua Simplex implementation already made and licensed under MIT, perfect for our purposes.

Setting up a procedurally generated world was fairly straight forward. First we generated a flat plane of evenly spaced vertices, then ran the noise generator against each vertex to get a height value (Z-position). To tie the tank to the ground, we cast a ray from the tank's centre into the

Read More →