Jul 9, 2013

Goal based vector field pathfinding in Haxe

Here is nothing to say. Everything was clearly described here.
Here is jus my code in haxe implementing GBVF pathfinding class.
package com.nailedgames;

import flash.geom.Point;


typedef PathTile = {
    var x:Int;
    var y:Int;
    var step:Int;
}

class GBVF_pathfinding
{
    private var _tile_width:Int;
    private var _tile_height:Int;
    private var _field_width:Int;
    private var _field_height:Int;
    private var _field_wtiles:Int;
    private var _field_htiles:Int;

    private var _distances:Array<Int>;
    private var _vectors:Array<Point>;

    public var vectors(get,null):Array<Point>;

    public function new()
    {

    }

    function get_vectors():Array<Point>
    {
        return _vectors;
    }

    public function Init(tile_width:Int, tile_height:Int, field_width:Int, field_height:Int)
    {
        _tile_width = tile_width;
        _tile_height = tile_height;
        _field_width = field_width;
        _field_height = field_height;
        _field_wtiles = Std.int(field_width/tile_width);
        _field_htiles = Std.int(field_height/tile_height);
        _distances = new Array();
        for(i in 0..._field_htiles)
            for(j in 0..._field_wtiles)
                _distances.push(-1);

    }

    public function Calculate_field(walls_map:Array<Int>, x_block:Int, y_block:Int):Array<Point>
    {
        for(i in 0..._distances.length)
            if(walls_map[i] == 1)
                _distances[i] = -3;
            else
                _distances[i] = -1;
        Mark_tiles([{x:x_block, y:y_block, step:0}]);
        return _vectors;
    }

    private function Mark_tiles(points:Array<Pathtile>):Void
    {
        if(Lambda.empty(points))
            return;
        var neighbours:Array<Pathtile> = new Array<Pathtile>();
        for(point in points)
        {
            _distances[point.x + point.y*_field_wtiles] = point.step;
            if(point.x > 0)
            {
                if(_distances[(point.x-1) + point.y*_field_wtiles] == -1)
                {
                    neighbours.push({x:point.x-1, y:point.y, step:point.step+1});
                    _distances[(point.x-1) + point.y*_field_wtiles] = -2;
                }
            }
            if(point.x < (_field_wtiles-1))
            {
                if(_distances[(point.x+1) + point.y*_field_wtiles] == -1)
                {
                    neighbours.push({x:point.x+1, y:point.y, step:point.step+1});
                    _distances[(point.x+1) + point.y*_field_wtiles] = -2;
                }
            }
            if(point.y > 0)
            {
                if(_distances[point.x + (point.y-1)*_field_wtiles] == -1)
                {
                    neighbours.push({x:point.x, y:point.y-1, step:point.step+1});
                    _distances[point.x + (point.y-1)*_field_wtiles] = -2;
                }
            }
            if(point.y < (_field_htiles-1))
            {
                if(_distances[point.x + (point.y+1)*_field_wtiles] == -1)
                {
                    neighbours.push({x:point.x, y:point.y+1, step:point.step+1});
                    _distances[point.x + (point.y+1)*_field_wtiles] = -2;
                }
            }
        }
        Mark_tiles(neighbours);
        Assign_vectors();
    }

    private function Assign_vectors():Void
    {
        _vectors = new Array<Point>();
        for(y_tile in 0..._field_htiles)
        {
            for(x_tile in 0..._field_wtiles)
            {
                if(_distances[x_tile + y_tile*_field_wtiles] > 0)
                {
                    var left_dist:Int   = _distances[x_tile + y_tile*_field_wtiles]+1;
                    var right_dist:Int  = _distances[x_tile + y_tile*_field_wtiles]+1;
                    var up_dist:Int     = _distances[x_tile + y_tile*_field_wtiles]+1;
                    var down_dist:Int   = _distances[x_tile + y_tile*_field_wtiles]+1;

                    if((x_tile > 0) && (_distances[(x_tile-1) + y_tile*_field_wtiles] >= 0)) 
                        left_dist = _distances[(x_tile-1) + y_tile*_field_wtiles];

                    if((x_tile < (_field_wtiles-1)) && (_distances[(x_tile+1) + y_tile*_field_wtiles] >= 0)) 
                        right_dist = _distances[(x_tile+1) + y_tile*_field_wtiles];

                    if((y_tile > 0) && (_distances[x_tile + (y_tile-1)*_field_wtiles] >= 0))
                        up_dist = _distances[x_tile + (y_tile-1)*_field_wtiles];

                    if((y_tile < (_field_htiles-1)) && (_distances[x_tile + (y_tile+1)*_field_wtiles] >= 0))
                        down_dist = _distances[x_tile + (y_tile+1)*_field_wtiles];

                    _vectors.push(new Point(
                        left_dist - right_dist,
                        up_dist - down_dist));
                }else{
                    _vectors.push(new Point(0,0));
                }
            }
        }

    }
}
Here is usage example:
var tile_width:Int = 40;
var tile_height:Int = 40;
var field_width:Int = 800;
var field_height:Int = 600;
gbvf = new GBVF_pathfinding();
walls_map = [
 0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
 0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
 0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,
 0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,
 0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,
 0,0,0,0,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,
 0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
 0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,
 1,1,1,1,1,1,1,1,0,0,0,0,0,1,0,0,0,0,0,0,
 0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,
 0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,
 0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,
 0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,
 ];
gbvf.Init(tile_width, tile_height, field_width, field_height);
var vectors:Array<Point> = gbvf.Calculate_field(walls_map, x_block, y_block);
As a result you will have array of vectors coordinates relative to the center of tiles.

Feb 26, 2013

Movement of a player

Just my way to adjust player view depends on keys pressed.
That are indexes of our angles:



// let's define keys, which will move player (keyCode, state)

private static var keys:Array<Array<Int>> = [[37,0],[39,0],[38,0],[40,0],[69,0],[65,0],[68,0],[87,0],[83,0]];
// define array, where angles will be stored
private var angles:Array<Int>;
// adjust angles to special indexes

private function Init(e:Event):Void // Event.ADDED_TO_STAGE
{
    angles = new Array<Int>();
    angles[1] = 0;
    angles[3] = 45;
    angles[2] = 90;
    angles[6] = 135;
    angles[4] = 180;
    angles[11] = 225;
    angles[7] = 270;
    angles[8] = 315;
}


public function on_key_down(e:KeyboardEvent):Void // KeyboardEvent.KEY_DOWN
{
    for(key in keys)
        if(e.keyCode == key[0] && key[1] == 0)
            key[1] = 1;
}

public function on_key_up(e:KeyboardEvent):Void // KeyboardEvent.KEY_UP
{
    for(key in keys)
        if(e.keyCode == key[0])
            key[1] = 0;
}



public function on_enter_frame(e:Event):Void // Event.ENTER_FRAME
{
    var r_index:Int = 0;
    // up - down
    if(keys[2][1] == 1 || keys[7][1] == 1) {
        r_index += 1;
    }else if(keys[3][1] == 1 || keys[8][1] == 1){
        r_index += 4;
    }
    // left - right
    if(keys[0][1] == 1 || keys[5][1] == 1)
    {
        r_index += 7;
    }else if(keys[1][1] == 1 || keys[6][1] == 1){
        r_index +=2;
    }
    if(r_index != 0)
        this.rotation = angles[r_index];
}

What is pros of that way for me:
- you can define not only array of angles, but array of Bitmaps or Sprites
- you can redefine array of angles (Sprites) on the way without any of code overhead
- cleaner code as for me (just two if - else)

Aug 30, 2012

SimpleCache in HaXe

Made SimpleCache class based on Ant.Karlov tutorial using HaXe.
Here is class code:
package mmg;

class SimpleCache<t>
{
    public var instance(get, never):T;

    private var _targetClass:Class<t>;
    private var _currentIndex:Int;
    private var _instances:Array<t>;

    public function new(targetClass:Class<t>, initialCapacity:Int)
    {
        _targetClass = targetClass; // Base class of all of objects
        _currentIndex = initialCapacity - 1; // Index of current free object
        _instances = []; // Array of all instances

        // Fill it up
        for (i in 0...initialCapacity)
        {
            _instances[i] = getNewInstance();
        }
    }

    private function getNewInstance():T
    {
        return Type.createEmptyInstance(_targetClass);
    }

    private function get():T
    {
        if (_currentIndex >= 0)
        {
            // Returning free object from cache
            _currentIndex--;
            return _instances[_currentIndex + 1];
        }
        else
        {
            // If cache is empty we should create one more instance
            return getNewInstance();
        }
    }

    private function put(instance:T):Void
    {
        _currentIndex++;
        // If cache is overfilled
        if (_currentIndex == _instances.length)
        {
            // We should put instance to the back of the instances array
            _instances[_instances.length] = instance;
        }
        else
        {
            // Putting instance to the free cell of array
            _instances[_currentIndex] = instance;
        }
    }
}
Usage example:
// init cache
var cache:SimpleCache<ourobjectclass> = new SimpleCache(OurObjectClass, 50); 
// take on of instances
var tmp:OurObjectClass = cache.instance; 
tmp.doSomething();
// put object back to cache
cache.put(tmp);