• Register
Post tutorial Report RSS Making a Modern Vector Graphics Game (Updated)

This updated article, which now includes Unity C# script, will help game developers imagine a new retrograde trend in game graphics, inspired by the vector graphics games of the 1980s. Games such as Battlezone, Tailgunner and the original Star Wars set the stage for more modern 3D games, but the unique style based on the limitations of the era was lost. Hoping to bring this style back into modern games, the article will demonstrate how vector based games can be made with modern gaming engines

Posted by on - Intermediate Design/Concepts

Making a Modern Vector Graphics Game
by Michael Klaus Schmidt


Update: I have added a section for using GL Lines in C# for folks who use Unity.

For many years, pixel graphics have been the style of choice for many games. From the earliest days of video games, large pixels were the format of choice (and necessity) for many games, and this style has worked its way into the psyche of many players. When “retro” video games became a thing of importance, it was natural that they would choose to use large pixels for their graphic assets and art.

Most game engines have ready made systems to use 2D sprites, and making games “pixel perfect” is no longer as difficult as it once was. This makes it a perfect choice for smaller scale developers and studios who do not have the time or budget to make high resolution, realistic models.

This screenshot is from one of my favorite pixel art games "Lovecraft's Untold Stories" Lovecraft's Untold Tales

However, there is another completely different graphic style, that may actually pre-date pixel graphics, that has gone largely unacknowledged in recent years. Vector Graphics games like Battlezone, Asteroids, Tempest, Tailgunner and even the very first Star Wars game used to dominate the arcades in the early 1980's. In fact, these games were the precursors to modern 3D games, using vectors to draw lines in a virtual 3D space.

So, for me, it seems natural that this style would also make a comeback. However, unlike 2D sprites, most game engines do not have a ready-made method of producing vector images. This is a problem, because without this kind of easy access, the trend, and potential swarm of new games, will be stunted before it gets a chance to take off and reach its full potential.

So I have come up with a method of producing vector graphics in a modern game engine. Now, before I begin explaining how to approach this, I first would like to say that there are probably other, equally valid, potentially better ways to do this. This is the method I used, but that should not limit anyone from exploring their own preferred way of doing things.

Second, I am not using one of the more popular game engines, so while my overall approach should work regardless of the engine used, the specific scripts I'm using will not.

Note: This method describes how to make vector games for standard graphics displays. This is not the same way vector games were created originally, which used oscilloscopes, or other specific pieces of graphic hardware, to generate true vector graphics.

Here is a screen capture of my latest creature in "Paradox Vector"Image from my Vector Graphics game

3D Models

To generate vector graphics on screen, we really need to focus on two main points. The first is the 3D models. Originally, vector games probably did not have “models” in the modern sense, and objects would have been generated through some kind of script. This is just speculation though, as I never learned how vector games were made in the early days of game development.

For modern engines, we generally use models to render 3D objects, and I thought this would be the best way to do it. However, our models will need to be created in a way that is different from those of most games. The main difference is that we do not use textures to render the model/mesh. I actually do use a texture, but it is solid black, and therefore cannot really be seen in-game.

So, I start by making a low-poly model of my game object, just as is done in any game. In theory, the models do not need to be low poly. I really think there could be some very unique things one could do with higher resolution models done in vectors, since that is something I've never seen before, but for now, and for the sake of simplicity, we will stick with low poly models.

This is the 3D model I used to generate my "Old One."This is the model I used to render my Old One

Once the model is done, I have to think about how I want to draw it. Since the model will not be visible, being solid black against a black background, I have to think about where each vector will be located and how it should connect to other vectors, i.e. where the lines will be drawn.

Why not just use the existing vectors in the model you ask? Well, the way I am rendering these models, it will connect each vector to the vector that was made right after it, in my game engine (3D GameStudio) vectors are numbered. So, in order to render correctly, each vector has to be produced in the order in which the lines will be drawn. Also, if the vector is also used to make a face for the model, if that face is solid (occluding shapes drawn behind it) then it will also occlude the lines being drawn, and they may appear imperfect.

So, to avoid these problems, I came up with the following method. Starting with my basic model, I then start adding new vertices carefully, one after another, just at those points where I want a line to be drawn. I will add them just outside the original “structural” vertices of the model, so the lines are not cut off by the faces of the mesh.

Another important aspect to this is that I am not connecting these “drawing vectors” in the model in any way. They are going to be used by the script to draw this object, but they should not be used to form faces. Rather they will be “floating” just above the surface of the model.

This is a close-up of the same model, showing the "floating vectors."hows additional

Also, since you do have a solid, black object to work with, you can also bring some of the vectors inside of that object. These inside lines will not be drawn, but this can be used as a way to re-position the line without having to create a complicated script that stops the line from being drawn between certain vectors. Of course, that would be another potential way to handle doing this.

Using Line Draw (Lite-C Language)

So the main tool we will use to render our 3D scenes is called a line-draw command. What I am doing is taking each game object, and for each frame, calling a script that draws all the lines that render the object. First, for each model, I will need to keep track of the number of the vertices used for rendering. So, say my base model uses 30 vertices for all the solid faces. Vertex number 31 will be the first “floating” vertex used for drawing. In my script for that object, I will use that number, and send it along to the drawing script, along with other important things like color and alpha.

The drawing script takes the information sent by the object, and uses it to render each line with a line-draw command. So it creates a while loop. In 3D Gamestudio, which uses the Lite-c language, loops can work one of two ways. If you put a wait(1); command at the end of the loop, it will run through the loop once for each frame. This is useful in most cases, because you don't want the game to get caught in the loop, thereby freezing the game. However, when you know the loop will be running a finite number of times, it is also acceptable to leave out the wait(1); command. This will cause the script to run through each iteration of the loop without waiting until the next frame, and this is just what we will do here. Depending on your game engine and/or scripting language, this may be handled differently, so you will need to look into that.

This is the script I use to draw vectors

Now in my script, it looks at the first vector I want to draw, and starts there. Then, it increments to the next vector, and draws a line between the first and second. Then, it draws a line between the second and third, and so on until it reaches the last vector. Then, when there are no more vectors to draw, the loop ends and it moves on to the next part of the script.

Here is a breakdown on how the script works. I'm using the same lines as those in the code shown above to describe what it's actually doing. With this, you may be able to convert this script into the language of your choice.

769 Declaration of the functions and its parameters, which include the three RGB colors it will be drawn with, its alpha (transparency) and the number of the starting vector. This function will be called by the game object being drawn once per frame, and it will look like this "draw_me(0,0,255,100,31);" In this example, it will draw a red game object, with no transparency, starting from the 31st vector of the model.
771 Declaring a Vector (set of three variables x,y,z) called line_pos
772 Declaring a Variable called line_x and initializing it to the start_vec position
773 Declaring a Variable called vert_num which is assigned "ent_status(my,1)" which returns the total number of vertices in the given model.
775 Starting a while loop, which will continue until the last vertex (vert_num) is reached
777 Assigns the position (x,y,z) of the current vertex to the Vector line_pos
779 if this is the very first line being drawn...
781 draw the line but do not assign any color to it. This will position the line at the first vertex, but will prevent a line from being drawn between it and the last object that was drawn. This is a particular feature of the game engine being used, and may or may not be necessary in other engines.
783 if this is the last vertex in the mesh...
785 It assigns the position of line_pos back to the first drawn vertex. This is another thing particular to the way I designed my meshes. I tried to add the last vertex right over the very first one, so I would have a sort of closed mesh. There are other ways to approach this, and you may want to allow for open meshes, so this would not be a necessary step.
788 else...
790 Draw the line from the end of the last line drawn to the current vertex position
793 Increment to the next vertex position
796 Reset the currently selected vertex position. I believe this step is unnecessary, and I'm not exactly sure why I added it to my script. However, since the script works right now, I will not worry about fixing/changing it :)

Using GL Lines (Unity C#)

I have taken some time to see about how this can be accomplished in C#, which is the default language in Unity, a more popular game engine than 3D Gamestudio. It turns out that it is not that different in terms of how to do it. Note, there are already some line drawing shaders on the asset store, but from my experience they will draw lines between all the vertices of a given mesh, and will not allow you to select which vertices are used. The script I am including works the same way as the method described above, so you will need to have a mesh prepared in a similar manner in order to get the results you want. There is more about how to prepare a mesh for use in Unity further along.

Screenshot

Above you can see that I used the FPS shooter tutorial game in Unity to test out my script. Also, I should give credit to the folks who made the Mesh Wireframe Renderer Asset on the Unity Asset Store.

Here is the script I created based on that script in order to get this to work...

using System.Collections.Generic;
using UnityEngine;

[RequireComponent( typeof( MeshFilter ) )]
public class Vectorlines: MonoBehaviour
{

//this method allows for two separate colors to be drawn on one model
//since different models may have different vertex numbers, I made these public so they
//can be assigned in the editor
public int drawStart;//Stores the first vertex to be drawn in the first color
public int drawStop;//stores the last vertex to be drawn in the first color

public int drawStart2;//Stores the first vertex to be drawn in the second color
public int drawStop2;//stores the last vertex to be drawn in the second color

private int _drawQueue = 0;
private string s = "";

//This method uses materials to choose the colors
//assign a color in the editor to each of these slots
//the names are arbitrary, they both do the same thing,
//but one will be used for the first set of vertices, and the other for the second
public Material MeshMaterial;
public Material WireMaterial;

public Vector3[] renderQueue;
private int myVertices;

Renderer m_Renderer;
MeshFilter meshFilter;
Mesh mesh;

//Start will initialize the mesh and determine the locations of each vertex
void Start()
{
meshFilter = gameObject.GetComponent();
mesh = meshFilter.mesh;
m_Renderer = gameObject.GetComponent();

//CREATE AN ARRAY TO STORE VERTEX INFORMATION
renderQueue = new Vector3[mesh.vertexCount];
myVertices = mesh.vertexCount;
for(var i = 0; i < myVertices; i += 1)
{
renderQueue[i]= mesh.vertices[i];
}
}

void OnRenderObject()
{
if (m_Renderer.isVisible)
{
GL.MultMatrix( transform.localToWorldMatrix );

//Begin drawing first set of lines
GL.Begin(GL.LINES);
MeshMaterial.SetPass(0);
GL.Color(new Color(0f, 0f, 0f, 1f));

_drawQueue = drawStart;
while(_drawQueue < drawStop)
{
GL.Vertex3( renderQueue[_drawQueue].x, renderQueue[_drawQueue].y, renderQueue[_drawQueue].z );
GL.Vertex3( renderQueue[_drawQueue + 1].x, renderQueue[_drawQueue + 1].y, renderQueue[_drawQueue + 1].z );

_drawQueue += 1;
}

GL.End();

//begin drawing second set of lines

GL.Begin(GL.LINES);
WireMaterial.SetPass(0);
GL.Color(new Color(0f, 0f, 0f, 1f));

_drawQueue = drawStart2;
while(_drawQueue < drawStop2 - 1)
{
GL.Vertex3( renderQueue[_drawQueue].x, renderQueue[_drawQueue].y, renderQueue[_drawQueue].z );
GL.Vertex3( renderQueue[_drawQueue + 1].x, renderQueue[_drawQueue + 1].y, renderQueue[_drawQueue + 1].z );

_drawQueue += 1;
}
GL.End();
}
}
}

Simply save this script and name it "Vectorlines.cs" and then add it as a component to the game object(s) you want to draw with vector graphics in the editor. You will need to apply two materials to the material slots, if you want both colors to render. You will also have to assign a starting and stopping point for each of the two sets of lines to be rendered.

Inspector2

Above is what you should see in your game object through the inspector. Remember the Draw Start and Draw Stop numbers will depend on the mesh you created, and may change from one mesh to another.

For a simpler object you may opt to only use one color. Then you would just leave "Draw Start 2" and "Draw Stop 2" with a value of 0. In that case you could also leave the "Wire Material" slot empty as well.

Important Notes about Unity

One very important step is to make sure you check the "Read/Write Enabled" box in the inspector once you have imported your model. This may be selected by default, I'm not sure, but make sure it is selected, or the game will not be able to reference the vertices of your mesh once your game has been published to an executable file. It WILL work in the editor without this box checked, but it will NOT WORK after publishing if the box is not checked.

Inspector

Using Blender

Also, in the example model above I used the 3D editor that comes with 3D Gamestudio. Unity, however, will not be able to use those models. Instead, you can use Blender, which allows you to import models directly into Unity. In fact, Blender is a great 3D tool, and it can do tons of great things, more than enough for any indie game developers.

One great thing about Blender, in reference to this tutorial, is that it can display the number of each of the vertices in your mesh. This will help when building your game objects. This option is not there by default though, so you will have to use Debug Mode in order to access it.

  • Start Blender in debug mode, or simply go to Python Console (Shift-F4)
  • Type "bpy.app.debug = True"
  • Go to N-panel > Mesh Display
  • A checkbox Indices will now show

Indices refers to the number of each vertex. This number is normally not important to game developers and 3D artists, but it is essential when using this method to draw game objects. If you do not create the object with the vertices made in the proper order, they will not draw correctly. You do not need to activate indices in Blender for this to work, but it helps when you are creating a model, to see the number of the vertex you're working on...

Indices

I'm currently using Blender version 2.76b.

Conclusion

This is not exactly a built in way to generate vector graphics. However, it does work, and it is not much more complex than designing any other aspect of game graphics. If carefully placing vectors in order seems time consuming, well, it is, but not much more so than generating UVs and texture maps.

Perhaps, some day, a game engine designer will come up with a more straightforward way to do this. Certainly, there are shaders that can be used to create somewhat similar effects, but they do not quite capture the original simplicity of vector graphics. Until then, I hope maybe some other developers might want to try this out. With modern computers, I think the potential to generate interesting and complex vector games has a huge creative potential that is largely untapped.

As a stylistic approach, vector graphics have the same potential nostalgia as pixel graphics, especially for older gamers. They say limitations are the source of creativity, and by limiting the way we render a game could generate whole new ways of thinking about game graphics.

This is a waterfall scene from "Paradox Vector". Like the impossible artwork of M.C. Escher, this waterfall flows around and feeds itself!A Vector waterfall from Paradox Vector

Post comment Comments
momoguru
momoguru - - 21 comments

very cool :)

Reply Good karma Bad karma+4 votes
Seamus.Editor
Seamus.Editor - - 152 comments

Super interesting! I really hope we see more games use vector graphics with modern engines and design, will be checking out Paradox Vector!

Reply Good karma Bad karma+2 votes
MKSchmidt Author
MKSchmidt - - 48 comments

Thanks! I hope to update it and have it ready for full release some time before 2020.

Reply Good karma+3 votes
ddadd
ddadd - - 115 comments

Vector graphics worked the same way you draw a cube in art class, without erasing any lines. The vector lines had a basic x and y value, as well as a z different value, that would also effect the size of the line. Using these basic principles, they could detect whether or not a player was running into something by comparing the z position of the enemy and the player, could distort lines to make them look like they rotated, or made lines always connect at one point so they could form together to make shapes. Pretty cool stuff. They used similar code with wolfenstein 3d, but used sprites in position instead of groups of lines.

Reply Good karma Bad karma+1 vote
Guest
Guest - - 689,493 comments

Surely an easier way would just be to edit the model's textures and draw solid black with neon edges? Most graphics software have a find edges filter built-in.

Reply Good karma Bad karma0 votes
MKSchmidt Author
MKSchmidt - - 48 comments

Easier, yes, but that would not be vector graphics. It's fine, and there are games that utilize shaders this way. But I felt like they were missing that specific look. Maybe I was able to get closer, maybe not, but it is something I felt was worth trying.

Reply Good karma+1 vote
Guest
Guest - - 689,493 comments

This comment is currently awaiting admin approval, join now to view.

Post a comment

Your comment will be anonymous unless you join the community. Or sign in with your social account: