Changing the plan

A little progress on a new game and an update to my dither photoshop plugin.

As a game developer there are many decisions that need to be taken prior to writing any code and one of the major ones is choosing the platforms you wish to release your game on and therefore choosing the right engine to use.

Back in the day it was about the machines capability which for me was the Commodore 64 with its hardware sprites and character graphics among other things, and back then there weren’t any development environments like you have today and life was simpler yet tougher on game developers, as we had to hand code 6510 assembly directly into the machines memory using a monitor cartridge. And if the power went or you some how locked the machine into a non-recoverable state all your changes would be lost, and if you did manage to remember to save your memory to tape, it would take ages to save.

Things got a little easier as time went on what with mass adoption of disk drives (floppies) and the development of macro assemblers eventually leading me to use a Commodore 128 to code on and a Commodore 64 connected via a homemade serial lead as a test machine. And by time I got onto the Amiga and Atari St there were PC based development kits like SNASM which worked in a similar manor to my old home-made development system via a cable.

And since then I have used many different SDK’s and game engines, ranging from Java and SDL all the way to Unreal engine 4, with my last game skyfight.io making use of the pixijs engine, while all of my mobile games I used the now defunct Marmalade engine.

Most developers usually use one engine and stick with it, limiting they knowledge base to one system and one system only, whereas having learned my craft over 36 years ago which was a good 23 years before Unity 1.0 was released, I tend to go for a code-based development environment rather than a graphical one, that said I did have some fun with UE4 for a while, before I realised my art was not up to the standard required to make a UE4 game stand out from the rest.

Currently I am using Gideros engine to develop on, which to many may appear to be a strange choice, however it fits my criteria for this game. Multi platform (Android,iOS,PC (maybe xBox one),Mac and HTML5 (Facebook) and the pricing is free, another win is that it is a code-based approach to game dev even if its in Lua.

At the moment Gideros ticks all the above boxes for me and yes I am aware that many other engines such as UE4 and Unity tick many of the boxes too but I am a coder and prefer to code everything by hand.

Now do not misunderstand me, if I had to use the Gideros editor to code my game I would seriously go nuts as it’s not much better than using wordpad and those days are well and truly in the past for me. I have used Visual studio for many years and just love it, therefore it was worth my time to see if I can use Visual studio with Gideros.

After a little messing around I opted for a simple cheat with using Gideros with Visual studio, which is basically use Gideros studio for the debugging and project management and use visual studio as the text editor. As I said its quite simple, as all I had to do is install a LUA plugin to visual studio to give me the syntax highlighting and colouring that make my programming day less of a stress. Then just create a project in Gideros studio and another blank project in Visual studio and add all the files used in the Gideros project. Too much effort and it’s not integrated I hear you say, so the question I have to ask is would you use word or notepad to write a design document?

Simple really just edit the code in Visual studio then save, click the run button in Gideros, which is not ideal I know, but hey at least I am not programming directly into a Commodore 64’s memory and forgetting to save.

So where are we with the game?

I am still playing with the art by making experimental block sets however during this phase realised that skyfight is not going to get as much traffic as I was expecting. And all the promises made by gamedistribution.com are basically worthless, as they were supposed to push it out to all the HTML5 websites and do some promotion and as of today’s date they have done little or no promotion and their developers console is showing hardly any traffic and most of my players are coming via direct hits from initial release. Which leaves me thinking that another fighting MMO would have the same result and would not be worth the effort in making it.

It’s a pretty good job i did not reveal what kind of game I was making. 🙂

Anyhow in the mean time I have updated the my dithering plugin for photoshop allowing for different size rectangles spread over up to 10 layers.

The updated Code
#target photoshop
#target photoshop

var     numLayers       =   4;
var     rectWidth       =   8;
var     rectHeight      =   8;

var     layers          =   0;

var abort       = false;
var exitPanel   = false;
var windowResource = "palette {  \
                            orientation: 'column', \
                            alignChildren: ['fill', 'top'],  \
                            preferredSize:[300, 130], \
                            text: 'Dither panel',  \
                            margins:15, \
                            \
                            optionsPanel: Panel { \
                                orientation: 'row', \
                                alignChildren: 'right', \
                                margins:15, \
                                text: ' PANEL ', \
                                ditherLayersLable: StaticText { text: 'Layers: ' }, \
                                ditherLayers: EditText { text: '4', characters: 6, justify: 'left', active: true }, \
                                ditherWidthLable: StaticText { text: 'Dither pixel width: ' }, \
                                ditherWidth: EditText { text: '8', characters: 6, justify: 'left', active: true }, \
                                ditherHeightLable: StaticText { text: 'Dither pixel height:' }, \
                                ditherHeight: EditText { text: '8', characters: 6, justify: 'left',active:true } \
                                } \
                            \
                            bottomGroup: Group{ \
                                stopButton: Button { text: 'stop', properties:{name:'stop'}, size: [220,24], alignment:['left', 'center'] }, \
                                cancelButton: Button { text: 'Cancel', properties:{name:'cancel'}, size: [220,24], alignment:['right', 'center'] }, \
                                applyButton: Button { text: 'Apply', properties:{name:'ok'}, size: [220,24], alignment:['right', 'center'] }, \
                            }\
}"
var         win          = new Window(windowResource);
win.bottomGroup.stopButton.onClick = function() {
  abort   =   true;
};
win.bottomGroup.cancelButton.onClick = function() {
  exitPanel   =   true;
  return win.close();
};
win.bottomGroup.applyButton.onClick = function() 
{
    if(app.preferences.rulerUnits != Units.PIXELS)
    {
        alert("Rulers must be set to pixels");
    }
    else if(parseInt( win.optionsPanel.ditherLayers.text) < 2 || parseInt( win.optionsPanel.ditherLayers.text) > 8 || isNaN(parseInt(  win.optionsPanel.ditherLayers.text)))
    {
        alert("Layers must be between 2 and 8");
    }
    else if(parseInt(  win.optionsPanel.ditherWidth.text) < 1 || parseInt( win.optionsPanel.ditherWidth.text) > app.activeDocument.width || isNaN(parseInt( win.optionsPanel.ditherWidth.text)))
    {
        alert("Width must be be divisible by the width of the document");
    }
    else if(parseInt( win.optionsPanel.ditherHeight.text) < 1 || parseInt( win.optionsPanel.ditherHeight.text) > app.activeDocument.height || isNaN(parseInt( win.optionsPanel.ditherHeight.text)))
    {
        alert("Height must be be divisible by the height of the document");
    }
    else
    {
        numLayers       =   parseInt( win.optionsPanel.ditherLayers.text);
        rectWidth       =   parseInt( win.optionsPanel.ditherWidth.text);
        rectHeight      =   parseInt( win.optionsPanel.ditherHeight.text);
        abort   =   false;
        createDithers();
    }
};
win.show();

while (exitPanel === false) 
{
    app.refresh(); // or, alternatively, waitForRedraw();
}

// exported from photoshop listener
function DrawShape() {

    if(abort==true)
    {
        return;
    }
    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.0); // R
    color.putDouble(charIDToTypeID("Grn "), 0.0); // G
    color.putDouble(charIDToTypeID("Bl  "), 0.0); // 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
function    addShape()
{
    if(abort==true)
    {
        return;
    }
    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 );
}

function    createDithers()
{
    // loop to add the pixels once for postions on each layer;
    layers          =   0;
    var y           =   0;
    var y           =   0;
    var numHeight   =   Math.floor(app.activeDocument.height/rectHeight);
    var numWidth    =   Math.floor(app.activeDocument.width/rectWidth);

    // create the array for a a bucket check to see if there is a pixel in this position
    var sArray      =   new Array(numHeight);
    for (y = 0; y < numHeight; y++) 
    {
        sArray[y]  =   new Array(numWidth);
        for (x = 0; x < numWidth; 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     layercount          =   0;
    var     maxRectsPerLayer    =   parseInt((numWidth*numHeight)/numLayers)-1;
    var     breaker = 0;
    while(layers < numLayers)
    {
        if(abort==true)
        {
            break;
        }
        layercount  =   0;
        var x       =   Math.floor(Math.random() * (numWidth));
        var y       =   Math.floor(Math.random() * (numHeight));
        if(sArray[y][x]==false)
        {
            DrawShape([x*rectWidth,y*rectHeight],[(x*rectWidth)+rectWidth,y*rectHeight],[(x*rectWidth)+rectWidth,(y*rectHeight)+rectHeight],[x*rectWidth,(y*rectHeight)+rectHeight]);   
            for (d = 0; d < numWidth*numHeight; d++) { x= Math.floor(Math.random() * (numWidth)); y= Math.floor(Math.random() * (numHeight)); if(sArray[y][x]==false) { sArray[y][x] = true; addShape(y*rectHeight,x*rectWidth,(y*rectHeight)+rectHeight,(x*rectWidth)+rectWidth); layercount++; if(layercount>maxRectsPerLayer)
                        {
                            break;
                        }
                    }
            }
            layerIndex++;
            layers++;
        }
        // this is just in case the same random pisel comes up and its a never ending loop
        breaker++;
        if(breaker>numLayers*4)
        {
            alert("Braked!");
            break;
        }
    }
    // now on the final layer filling in the gaps
    for (y = 0; y < numHeight; y++) 
    {
        if(abort==true)
        {
            continue;
        }
        for (x = 0; x < numWidth; x++) 
        {
            if(abort==true)
            {
                continue;
            }
            if(sArray[y][x]==false)
            {
                sArray[y][x]    =   true;
                addShape(y*rectHeight,x*rectWidth,(y*rectHeight)+rectHeight,(x*rectWidth)+rectWidth);
            }
        }
    }
    alert("All Done!");
}