May 3, 2015

InWeb adventures

Today i decided to start my new small project.
I will create web-based game - some sort of multiplayer survival game (something like die2nite).

I will use haxe for it.
Will try to make it using:

  • ufront
  • jive


So, that's it, in short words.

Dec 7, 2013

Usage of particle manager

So, here is a little how-to for subject.
First you need ParticleManager instance in your class (smoke for example)

smoke = new ParticlesManager(1000, ParticleState.Update_particle, true);
emitterPosition = new Point(stageWidth + 40, stageHeight / 3);
addChild(smoke);

Then in some loop of your game (EnterFrame?) you have to emit particles

var speed_x:Float = -20 * (Math.random()) - 20;
var speed_y:Float =  14 * (Math.random()) - 7;
var state:ParticleState = new ParticleState(new Point(speed_x, speed_y), 1);
smoke.Create_particle(Assets.getBitmapData("assets/round_20.png"), 
 new Point(emitterPosition.x, emitterPosition.y),
 0xff0000ff, 30, new Point(0.1, 0.1), state, 0, 0.2, 0.04); // look at ParticleManager code for params description

As you can see, you need ParticleState class for particles

package;

import flash.geom.Point;
import particles.Particle;

class ParticleState
{
    public var velocity:Point;
    public var lengthMultiplier:Float = 0.0;
 
 public function new(velocity:Point, lengthMultiplier:Float)
 {
  this.velocity = velocity;
  this.lengthMultiplier = lengthMultiplier;
 }
 
 public static function Update_particle(particle:Particle):Void //here you have to define how particles move
 {
  var vel:Point = particle.state.velocity;
  particle.position.x += vel.x;
  particle.position.y += vel.y;
  particle.bitmap.scaleX += (1 - particle.bitmap.scaleX) * particle.sizeReduction;
  particle.bitmap.scaleY += (1 - particle.bitmap.scaleX) * particle.sizeReduction;
  particle.bitmap.alpha -= particle.fading;
 }
}

Aug 28, 2013

Particles manager from gamedev.tutsplus.com

Made simple implementation of Particles manager for my game based on this article.
Here is Source code for it.

ParticlesManager.hx

package mmg.particles;
package particles;

import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;
/**
 * ...
 * @author gordev
 */
typedef Action = Particle -> Void;

class CircularParticleArray
{
 private var _start:Int;
 private var _list:Array;
 private var _targetClass:Class;
 private var _args:Array;
 
 public var start(get, set):Int;
  public function get_start():Int { return _start; }
 public function set_start(value:Int) { _start = value % _list.length; return 0; }
 public var capacity(get, never):Int;
 public function get_capacity():Int { return _list.length; }
 public var list(get, never):Array;
 public function get_list():Array { return _list; }
 public var count:Int = 0;

 public function new(targetClass:Class, initialCapacity:Int, args:Array)
 {
  _targetClass = targetClass;
  _list = [];
  _args = args;
  
  for (i in 0...initialCapacity)
        {
   _list.push(Type.createInstance(_targetClass, _args));
  }
 }
 
 public function get(i:Int):T
 {
  return _list[(_start + i) % _list.length];
 }
 
 public function set(i:Int, value:T):Void
 {
  _list[(_start + i) % _list.length] = value;
 }
}

class ParticlesManager extends Sprite
{
 private var update_particle:Action;
 private var particle_list:CircularParticleArray;
 
 public function new(capacity:Int, update_particle:Action, isDisplayList:Bool) 
 {
  super();
  this.update_particle = update_particle;
  particle_list = new CircularParticleArray(Particle, capacity, []);
  if(isDisplayList)
  {
   for(particle in particle_list.list)
   {
    addChild(particle.bitmap);
   }
  }
 }
 
 public function Create_particle(texture:BitmapData, 
  position:Point, 
  color:UInt, 
  duration:Float, 
  scale:Point, 
  state:Dynamic, 
  theta:Float = 0, 
  sizeReduction:Float = 1,
  fading:Float = 1):Void
 {
  var particle:Particle;
  if (particle_list.count == particle_list.capacity)
  {
   // if the list is full, overwrite the oldest particle, and rotate the circular list
   particle = particle_list.get(0);
   particle_list.start++;
  }else{
   particle = particle_list.get(particle_list.count);
   particle_list.count++;
  }
  
  // Create the particle
  particle.texture = texture;
  removeChild(particle.bitmap);
  particle.bitmap = new Bitmap(texture);
  addChild(particle.bitmap);
  
  particle.position = position;
  particle.color = color;
  particle.duration = duration;
  particle.percent_life = 1.0;
  particle.scale = scale;
  particle.bitmap.scaleX = scale.x;
  particle.bitmap.scaleY = scale.y;
  particle.orientation = theta;
  particle.state = state;
  particle.sizeReduction = sizeReduction;
  particle.fading = fading;
 }
 
 public function Update()
 {
  var removal_count:Int = 0;
  var index:Int = 0;
  for (i in 0...particle_list.count)
  {
   var particle:Particle = particle_list.get(i);
   update_particle(particle);
   particle.percent_life -= 1.0 / particle.duration;
   Swap(particle_list, index - removal_count, index);
   if (particle.percent_life < 0) removal_count++;
   index++;
  }
  particle_list.count -= removal_count;
 }
 
 public function Draw(canvas:Bitmap)
 {
  canvas.bitmapData.fillRect(canvas.bitmapData.rect, 0x00000000);
  for (i in 0...particle_list.count)
  {
   var particle:Particle = particle_list.get(i);
   var origin:Point = new Point(particle.texture.width / 2, particle.texture.height / 2);
   var mat:Matrix = new Matrix();
   mat.translate(particle.position.x - origin.x, particle.position.y - origin.y);
   canvas.bitmapData.draw(particle.texture, mat); //and so on
  }
 }
 
 public function Move()
 {
  for (i in 0...particle_list.count)
  {
   var particle:Particle = particle_list.get(i);
   particle.bitmap.x = particle.position.x - particle.bitmap.width  * 0.5;
   particle.bitmap.y = particle.position.y - particle.bitmap.height * 0.5;
  }
 }
 
 public function Swap(list:CircularParticleArray, index1:Int, index2:Int):Void
 {
  var temp:Particle = list.get(index1);
  list.set(index1, list.get(index2));
  list.set(index2, temp);
 }
 
 public function RenderBitmap(canvas:Bitmap):Void
 {
  Update();
  Draw(canvas);
 }
 
 public function RenderDisplayList():Void
 {
  Update();
  Move();
 }
}

Particle.hx

package particles;

import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.geom.Point;

/**
 * ...
 * @author gordev
 */
class Particle
{
 public var texture:BitmapData;
 public var bitmap:Bitmap;
 public var position:Point;
 public var origin:Point;
 public var orientation:Float;
 public var scale:Point;
 public var color:UInt;
 public var duration:Float;
 public var percent_life:Float;
 public var state:Dynamic;
 public var fading:Float;
 public var sizeReduction:Float;
 
 public function new()
 {
  bitmap = new Bitmap();
  scale = new Point(1, 1);
  percent_life = 1.0;
 }
}



I will write about usage and step by step description later.

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)