New Game

Thinking about it

So here we are at the start of a new game, making all the plans for how I am going to achieve this within the confines of a web browser again. I very rarely plan all the details of game play and mechanics as I find they come naturally as the game progresses and they always evolve over time, however I do spend a fair amount of time on the planning of the methods I will employ within the code. To this end I am working out in my mind how I can have a large map of block data within vertex limits of WebGL.

In this game I want to try to push things and have over 100 players at the same time on the same map, and I have a fair idea how I can do this using a modified copy of my https://skyfight.io game server. The game sever I wrote for Sky Fight is written in c++, so it will have no problem sending the data for 100 or even 200 players, it’s just how to handle that number of players on the client side, in JavaScript, in a browser while leaving enough bandwidth for bullets and non-player-controlled characters. Being as the game runs on server and the clients are basically dumb terminals, I think it will be easy to employ some sort of culling on the server-side to only updating the surrounding areas to the client’s window, much the same on how I handled all the bullets in Sky Fight.

Before we, start I want to say that I have been making games for over 35 years and in my day back in 1982, tile maps were called block maps and I will always see them as block maps with blocks, for me a tile is a flat surface with limited information such as texture and colour whereas a block is a block of information that can contain anything such as texture and animation and collisions. At the end of the day you can call them what you like but the map is just an array of indexes that are used to index a set of predefined blocks of information.

As for the maps, I have decided to go for a block map, however I want a layered effect so I have set upon one layer of blocks for the map with the standard item attributes to say what the block contains such as, are there trees, bushes, pick up items, buildings etc, however where the block has a tree or maybe a bridge I will draw them on the upper layer draw call rather than the lower. This should give me the ability to have the action going on under the tree canopy and bridges. Each block will have the ability to be linked to multiple sprites, and to create the illusion of depth on a top down game we must have shadows.

During this period, I also play with the art, as for me it’s the art that makes the game and my it is biggest headache, I am sure that some developers tend not to worry so much about the art but for me it has to be as good as can make it, especially being as I normally take a game genre and try to push it as far as I can to show how good it could be.

This game will be no different, I am taking a well-established genre of a multiplayer game and doing what I did with Sky Fight, pushing the limits while trying to make the best game I can. And no matter how good my code is, it’s all no good if the art is crap. Don’t misunderstand me, I am a coder first and my art is a secondary learned skill, therefore my art is not up there with the best, but I think it’s good enough, and it is the best I can do, so if I am happy with it then I am sure some of you will be too!

So, knowing the game is a top down view-point and without revealing what I kind of game I am making yet, let’s talk about what I am actually doing today.

The block set

Today I am experimenting with ground, grass and blending grass edges with ground blocks, I think I have come up with a way I can make some basic ground and grass along with a method of making the edges look natural.

I have opted for a dithered ground effects in multiple Photoshop layers of colours, and to achieve this so it looks random I decided to write a Photoshop script to create a series of shape layer masks.

I know some of you may be thinking I could use a Photoshop dithered layer, however I am not sure if you have noticed that the Photoshop dither is not random, it is the same for each layer and even if you use the layer alpha to create different amounts of dither it always removes the same pixels in a same order.

Photoshop

I choose to use shapes (vector) as much as possible as it allows me to scale objects without losing edge definition, leaving all the images crisp and sharp.

Working out the code to draw 5000 random 1×1 pixel shapes on a layer is not as hard as you may think, despite the Adobe documentation being really bad. There is a very useful plugin supplied by Adobe which is a actions listener which dumps out the source code in JavaScript or VBScript to your desktop.

Even though this page says its for Photoshop CC, this plugin runs with my copy of Photoshop CS6.

It is a simple task of recording how to make a shape on a layer and putting it into a loop with random x and y positions. Because I want to have my pixels on one layer and not the others have added an array of pixels so I can check if there is a pixel in that position already so I don’t draw in the same position twice!

The Code

#target photoshop

// exported from photoshop listener with the variables renamed to make sense
function DrawShape() {

    var doc = app.activeDocument;
    var y = arguments.length;
    var i = 0;

    var rectArray = [];
    for (i = 0; i < y; i++) {
        rectArray[i] = new PathPointInfo;
        rectArray[i].kind = PointKind.CORNERPOINT;
        rectArray[i].anchor = arguments[i];
        rectArray[i].leftDirection = rectArray[i].anchor;
        rectArray[i].rightDirection = rectArray[i].anchor;
    }

    var lineSubPathArray = new SubPathInfo();
    lineSubPathArray.closed = true;
    lineSubPathArray.operation = ShapeOperation.SHAPEADD;
    lineSubPathArray.entireSubPath = rectArray;
    
    var myPathItem = doc.pathItems.add("myPath", [lineSubPathArray]);

    var actionDescript = new ActionDescriptor();
    var contentLayer = new ActionReference();
    
    contentLayer.putClass(stringIDToTypeID("contentLayer"));
    actionDescript.putReference(charIDToTypeID("null"), contentLayer);
    var actionType = new ActionDescriptor();
    var actionColor = new ActionDescriptor();
    var color = new ActionDescriptor();
    color.putDouble(charIDToTypeID("Rd  "), 0.000000); // R
    color.putDouble(charIDToTypeID("Grn "), 0.000000); // G
    color.putDouble(charIDToTypeID("Bl  "), 0.000000); // B
    var rgbCoverted = charIDToTypeID("RGBC");
    actionColor.putObject(charIDToTypeID("Clr "), rgbCoverted, color);
    actionType.putObject(charIDToTypeID("Type"), stringIDToTypeID("solidColorLayer"), actionColor);
    actionDescript.putObject(charIDToTypeID("Usng"), stringIDToTypeID("contentLayer"), actionType);
    executeAction(charIDToTypeID("Mk  "), actionDescript, DialogModes.NO);
    myPathItem.remove();
}
// exported from photoshop listener raw output
function    addShape()
{
    var idAddT = charIDToTypeID( "AddT" );
    var desc16 = new ActionDescriptor();
    var idnull = charIDToTypeID( "null" );
        var ref5 = new ActionReference();
        var idPath = charIDToTypeID( "Path" );
        var idOrdn = charIDToTypeID( "Ordn" );
        var idTrgt = charIDToTypeID( "Trgt" );
        ref5.putEnumerated( idPath, idOrdn, idTrgt );
    desc16.putReference( idnull, ref5 );
    var idT = charIDToTypeID( "T   " );
        var desc17 = new ActionDescriptor();
        var idTop = charIDToTypeID( "Top " );
        var idPxl = charIDToTypeID( "#Pxl" );
        desc17.putUnitDouble( idTop, idPxl,  arguments[0] );
        var idLeft = charIDToTypeID( "Left" );
        var idPxl = charIDToTypeID( "#Pxl" );
        desc17.putUnitDouble( idLeft, idPxl, arguments[1] );
        var idBtom = charIDToTypeID( "Btom" );
        var idPxl = charIDToTypeID( "#Pxl" );
        desc17.putUnitDouble( idBtom, idPxl,arguments[2] );
        var idRght = charIDToTypeID( "Rght" );
        var idPxl = charIDToTypeID( "#Pxl" );
        desc17.putUnitDouble( idRght, idPxl, arguments[3] );
    var idRctn = charIDToTypeID( "Rctn" );
    desc16.putObject( idT, idRctn, desc17 );
    executeAction( idAddT, desc16, DialogModes.NO );
}
// loop to add the pixels once for postions on each layer;
var layers = 0;
var y       =  0;
var y       =  0;
// create the array for a a bucket check to see if there is a pixel in this position
var sArray  =   new Array(app.activeDocument.height);
for (y = 0; y < app.activeDocument.height; y++) 
{
    sArray[y]  =   new Array(app.activeDocument.width);
    for (x = 0; x < app.activeDocument.width; x++) 
    {
        sArray[y][x] = false;
    }
}
var     layerIndex      =   0;
// less trys on the first layer as there will be more rejections on the later layers
var     layercounts    =   [2000,3000,4000,5000,6000];
var     breaker = 0;
while(layers < 4)
{
    var x       =   Math.floor(Math.random() * (app.activeDocument.width));
    var y       =   Math.floor(Math.random() * (app.activeDocument.height));
    if(sArray[y][x]==false)
    {
        DrawShape([x,y],[x+1,y],[x+1,y+1],[x,y+1]);   
        for (d = 0; d < layercounts[layerIndex]; d++) 
        { 
          x= Math.floor(Math.random() * (app.activeDocument.width));
           y= Math.floor(Math.random() * (app.activeDocument.height));
           if(sArray[y][x]==false)
           {
           sArray[y][x] = true;
           addShape(y,x,y+1,x+1);
           }
        }
        layerIndex++; layers++;
     } 
// this is just in case the same random pixel comes up and its a never ending loop 
    breaker++; 
    if(breaker>10)
    {
        alert("Braked!");
        break;
    }
}
// now on the final layer filling in the gaps

for (y = 0; y < app.activeDocument.height; y++) 
{
    for (x = 0; x < app.activeDocument.width; x++) 
    {
        if(sArray[y][x]==false)
        {
            sArray[y][x]    =   true;
            addShape(y,x,y+1,x+1);
        }
    }
}
alert("All Done!");

To demonstrate the output I have coloured the layers, red, green, blue and yellow, with each pixel being a 1 x 1 square shape.