As we have seen in the last newsletter, we actually have already an engine and a base game that works quite well but has still one major problem.
We draw way too many lines for the Vectrex, so it flickers a lot!
How we can solve that?
Reduce the number of lines
Of course, we can not really reduce the number of lines overall; we need at any point in time to draw everything, but what we can do is reduce the number of lines we draw at the same time!
In our case, we have drawn the entire level already in the init function of the game. This was a brute-force approach, to begin with, and to show the concepts. But in reality that doesn’t make any sense. We will never see the lines that far away but they get drawn anyway (no matter how small they are).
The (simple) approach I use at first is to split the game into sections. We already use sections when we defined the level file, so that helps us to split the creation.
Let’s do that!
Remove Code from init()
The first we do is remove all the unnecessary code from vexxon_target_init()
.
while (level_has_more_lines()) {
level_line* line;
level_get_current_line(&line);
_create_section(line);
_distance += 10;
if (_space_area == -1 && line->border == 0) {
_space_area = count;
}
count++;
}
This code was responsible for creating all lines (objects) at the beginning and letting the engine draw every frame. So just delete that part!
Remove code from pre_render()
The second code block we don’t use anymore is the floor drawing in vexxon_target_pre_render()
. It was anyway just used in the playground app and not on the Vectrex but with the current changes we have in mind it doesn’t work anymore. So just delete it for now as well.
draw_setcolor(COLOR_FLOOR);
int offset = START_DISTANCE / 10;
for (int i = 0; i < _space_area + offset; i++) {
vec3 start = { LEFT_BORDER, 0, 5 + i * 10 };
vec3 end = { LEFT_BORDER + LEVEL_WIDTH, 0, 5 + i * 10 };
draw3d_line(start, end, *cam);
}
Add Code to Update()
Since we already have quite a good structure in the code it’s now very easy to create the level sections during the update. Also for that, there are several approaches to doing so, but one simple is to create every section based on the time elapsed since we last time drew a section. Of course, this time has to match the actual flight speed, which is a drawback. Another approach would be to check the distance we already made with the player and create the next section after a given distance. I choose the first one for now.
_add_next_section(delta);
This line we add at first in vexxon_target_update()
and as the function name implies we want to create the next section based on delta time.
void _add_next_section(float delta) {
_border_delay_time += delta;
if (_border_delay_time >= SECTION_TIME) {
if (level_has_more_lines()) {
level_line* line;
level_get_current_line(&line);
_create_section(line);
_distance += 10;
if (_space_area == -1 && line->border == 0) {
_space_area = _section_count;
}
_section_count++;
}
_border_delay_time = 0.0f;
}
}
As you see it’s quite similar to the code we removed before in vexxon_target_init()
but this time we just retrieve the next line from the level data after a given time (SECTION_TIME
) and call then the same function we already used before: _create_section(line).
The rest of the code remains exactly the same! This is also a good example of why it helps a lot to structure your code into small function blocks when you later want to refactor or change something. Let’s see how this looks.
At first with no player movements to see the creation of a section better:
And now while we move the player :)
Not bad already but we can do better!
Use Isometric Camera
Vexxon is a game that uses an isometric view but so far we render all from behind the player. While this change is needed for the final game, it also helps us to reduce the number of lines that are needed to draw.
An isometric view is actually quite easy to realize. We just change the follow camera view below
// Follow
cam->pos = vec3_make(0.0f, 2.0f, 0.0f);
cam->rot = vec3_make(0.0f, 0.0f, 0.0f);
to an isometric camera view by changing the position and rotation of the camera. E voila!
// ISO view
cam->pos = vec3_make(7.82f, 4.37f, 1.82f);
cam->rot = vec3_make(0.0f, -0.43f, 0.0f);
Not bad, right? But there is also another topic that remains open…
Delete Game Objects?
That already helped a lot on the Vectrex. Maybe you also ask yourself why we just don’t remove also the objects that are behind us and therefore never get visible again? Well, reducing the number of lines to draw, actually doesn’t help. We anyway never draw lines that are outside of the area of the screen. So we just waste CPU time here ;) Of course, that’s not a good approach but luckily this is not a limitation because we have plenty of it on the Raspberry PI and so we change that when we optimize the engine later.
Sidenote: If we would write code that runs directly on the Vectrex that would be a huge problem because we have just a given number of CPU cycles available between a screen draw and every statement uses a bunch of it. Also, a reason why most of the native Vectrex games are actually written in 6809 assembly language is to have even more control over the number of cycles used.
What’s Next?
Maybe you wondered why this post was sent as a “Sideletter” and not actually as an “official Newsletter”. Well, since last year I have already worked on a better engine that has much more features that help us simplify the later game, but also is the basis for other games I work on (hint: You have already seen the engine in the last Sideletter). I thought it makes sense to start the newsletter with the current engine because it helps to explain things more easily, but now it’s soon time to switch to the next level and use a C++-based update of the game engine.
I will also go into more details about the engine, but for now, we focus on the game and you will see, that the game code itself is very similar to what we have already
So stay tuned for Newsletter #5 which introduces the new game engine. After that, we add the code needed for the PiTrex and will add “Fire and Action” to the game code!
Great write-up! As a point of clarity, the final camera angle being used is a three-quarter view, not an isometric one. Zaxxon was in fact isometric. The difference being that a three-quarter view still has perspective and an isometric view does not have a vanishing point. Regardless, everything looks terrific and I can’t wait for the next chapter!
So the engine will need to run external. Going to use PiTrex, VeXTREME, or VecFever? Maybe build your own board or go USB connected to the Vectrex and run off the PC/Mac/Linux box?