Would a Map similar to the one in the pic below:
http://www.r-fate.com/screen2.jpg
be possible to generate in PHP?
Each of those blocks, the trees, the houses, etc needs to be generated based on information in a database. Ive never seen any php image library do something like this before so I'm just wondering if it's possible, and how hard (an estimate) it would be to do so.
Thanks.
php image generating experts
Moderator: General Moderators
it may not be as hard as you think, especially since these are blocks. you could make images with trees in it ( along with the half blocks you can see all around it ), and then generate graphing points out of the data you want. each point would be a tree, or house, or whatever you would specify ( wouldn't be much different from making a 3d bar chart in a way ). don't get me wrong, this would be a very long project to work with, but i'm sure it's possible at least 
That's what I was thinking, I could make full blocks and partial blocks for percentages.
I'm having a bit of trouble figuring out the theory behind placing everything where it belongs, maybe it's because of my lack of experience with image generating. But i'm pretty sure i would also need a database grid system so everything gets assigned a grid placement, so each time the map is generated it doesnt randomly put stuff in places. I would imagine that would make the whole mapping idea very realistic if it worked that way.
I'm having a bit of trouble figuring out the theory behind placing everything where it belongs, maybe it's because of my lack of experience with image generating. But i'm pretty sure i would also need a database grid system so everything gets assigned a grid placement, so each time the map is generated it doesnt randomly put stuff in places. I would imagine that would make the whole mapping idea very realistic if it worked that way.
-
pootergeist
- Forum Contributor
- Posts: 273
- Joined: Thu Feb 27, 2003 7:22 am
- Location: UK
The actual drawing of one image over another is *relatively* childs play.
The awkward part of the programming would be determining a starting x-pos and y-pos coordinate pair (more so if you chose to go a step further and use variable altitudes for the backing terrain)
The trig for determining a horizontal position and vertical from an isometric grid reference is standard enough though, which would return a good baseleft x-y pair - add an offset pair ² and then just run a function to do the overlay (sample below)
² - offset used for larger/smaller higher/taller overlays. You really want to have the top-y and left-x variables passed to the overlay function for ease of coding.
Sample overlay GD function with transparent colour facility
The exact protocols you use for storing retrieving information about items and item placement would sort of depend upon what the rest of the application does.
Subnotes:
I used imagesetpixel rather than imagecopymerge as it should be a touch faster and also the imagecopymerge function is fubared in 2.0.10 to 2.0.12 or thereabouts.
You should also note that using ping *.png files for the merge images is best as they can hold the transparent colour without speckling or loss. Try the same with jpegs and you would have some pixels show that were meant as transparent.
The awkward part of the programming would be determining a starting x-pos and y-pos coordinate pair (more so if you chose to go a step further and use variable altitudes for the backing terrain)
The trig for determining a horizontal position and vertical from an isometric grid reference is standard enough though, which would return a good baseleft x-y pair - add an offset pair ² and then just run a function to do the overlay (sample below)
² - offset used for larger/smaller higher/taller overlays. You really want to have the top-y and left-x variables passed to the overlay function for ease of coding.
Sample overlay GD function with transparent colour facility
Code: Select all
<?php
class MAPBUILD {
var $bgimage; // a pointer to the background image
function MAPBUILD($img='', $colour='CCCCCC', $width=360, $height=240) {
$this->bgimage = Null;
if(strlen($img) > 0 && file_exists($img)) {
// img reference sent - create from that
$this->giz = getimagesize($img);
$this->bgimage = ($this->giz[2] < 4) ? ($this->giz[2] < 3) ? ($this->giz[2] < 2) ? ($this->giz[2] < 1) ? Null : imagecreatefromgif($img) : imagecreatefromjpeg($img) : imagecreatefrompng($img) : Null;
} else {
// no img reference, create blank image
$this->bgimage = imagecreatetruecolor($width, $height);
$bgcolour = imagecolorallocate($this->bgimage, hexdec(substr($colour,0,2)), hexdec(substr($colour,2,2)), hexdec(substr($colour,4,2)));
imagefill($this->bgimage, 0, 0, $bgcolour);
}
return (is_null($this->bgimage)) ? false : true;
}
function mergeImage($merge_img="", $x_left=0, $y_top=0, $trans_colour="0000FF") {
$this->mi = $merge_img;
if(file_exists($this->mi)) {
$this->xx = $x_left;
$this->yy = $y_top;
$this->tc = array(
'red'=> hexdec(substr($trans_colour,0,2)),
'green'=> hexdec(substr($trans_colour,2,2)),
'blue'=> hexdec(substr($trans_colour,4,2))
);
$this->md = getimagesize($this->mi);
$this->mw = $this->md[0];
$this->mh = $this->md[1];
$this->mm = ($this->md[2] < 4) ? ($this->md[2] < 3) ? ($this->md[2] < 2) ? imagecreatefromgif($this->mi) : imagecreatefromjpeg($this->mi) : imagecreatefrompng($this->mi) : Null;
// we now have an overlay image stored in GD format as $this->mm
// with width $this->mw and height $this->mh
// iterate that image a pixel at a time
for($this->ypo = 0; $this->ypo < $this->mh; $this->ypo++) {
for($this->xpo = 0; $this->xpo < $this->mw; $this->xpo++) {
$this->indx_rgb = imagecolorsforindex($this->mm, imagecolorat($this->mm, $this->xpo, $this->ypo));
if(($this->indx_rgb['red'] == $this->tc['red']) && ($this->indx_rgb['green'] == $this->tc['green']) && ($this->indx_rgb['blue'] == $this->tc['blue'])) {
// transparent, so ignore
} else {
// not transparent colour, so overlay
$pixel_colour = imagecolorallocate($this->bgimage, $this->indx_rgb['red'], $this->indx_rgb['green'], $this->indx_rgb['blue']);
imagesetpixel($this->bgimage, $this->xx+$this->xpo, $this->yy+$this->ypo, $pixel_colour);
}
}
}
imagedestroy($this->mm);
return true;
} else {
return false;
}
}
}
/*********** SAMPLE UNDYNAMIC CALL
$a = new MAPBUILD;
$a->mergeImage('./imgs/1.png',50,50);
$a->mergeImage('./imgs/1.png', 150, 120);
$a->mergeImage('./imgs/1.png',250,50);
$a->mergeImage('./imgs/1.png', 10, 190);
header("Content-type: image/jpg");
imagepng($a->bgimage);
***********************************/
?>Subnotes:
I used imagesetpixel rather than imagecopymerge as it should be a touch faster and also the imagecopymerge function is fubared in 2.0.10 to 2.0.12 or thereabouts.
You should also note that using ping *.png files for the merge images is best as they can hold the transparent colour without speckling or loss. Try the same with jpegs and you would have some pixels show that were meant as transparent.
-
pootergeist
- Forum Contributor
- Posts: 273
- Joined: Thu Feb 27, 2003 7:22 am
- Location: UK
Looking back at that - repeated imageMerge calls on the same overlay image *should* really be handled by passing a multi-dim array into the imageMerge function.
You could probably also imagemap the locations while you were assembling the components, thus allowing people to click where they wanted to go and/or displaying alt/title text on mouseover.
You could draw the landscape in a similar fashion (starting at the top and working downwards a grid at a time) and save area background images locally to speed up the compile.
>> off to tinker with some altitude referencing - might have a better working model soon.
You could probably also imagemap the locations while you were assembling the components, thus allowing people to click where they wanted to go and/or displaying alt/title text on mouseover.
You could draw the landscape in a similar fashion (starting at the top and working downwards a grid at a time) and save area background images locally to speed up the compile.
>> off to tinker with some altitude referencing - might have a better working model soon.
-
pootergeist
- Forum Contributor
- Posts: 273
- Joined: Thu Feb 27, 2003 7:22 am
- Location: UK
Had a further tinker and opted to use nested objects for the overlay images -
As they sit within an associative array of the parent MAPBUILD object, they are only instantiated once. When instantiated they are assembled as an array[x-pos][y-pos] = array(RR, GG, BB, AA); of non transparent pixels.
Each object is then draw over the map by iterating its pixel array - should (and does) speed the process somewhat.
If you wanted to go astep further, I'd advise using the .gd2 file format rather than png (just slightly faster as more native to gd)
Anyway, http://www.teckis.com/testing/overlay/ovre.php uses five nested objects (images) to create the map shown....
Quite a bit of work to get that running nice from a database - perhaps
MAPS
`id` AUTO_INCREMENT
`name` VARCHAR
`desc` VARCHAR
`rows` SMALLINT 2
`cols` SMALLINT 2
MAP_ITEMS
`id` AUTO_INCREMENT
`map_id` MEDIUMINT 8
`type` SMALLINT 2 // eg 1=terrain, 2=groundItems, 3=aerial stuff
`image` VARCHAR
`row` SMALLINT 2
`col` SMALLINT 2
`altitude` SMALLINT 2
`x_offset` SMALLINT 2
`y_offset` SMALLINT 2
then you could store the terrain and items in the same table and order by row, col, type ASC.
That would also facilitate referencing any item to a grid rather than full x,y from top left.
Feel free to hack that around any way you want.
Have a major project at work and am moving house to a non-net-connected abode, so will not have much time to domuch more to this coding. Will try to keep an eye on things during my lunchbreaks , so if you meet a brickwall, just holler and wait.
As they sit within an associative array of the parent MAPBUILD object, they are only instantiated once. When instantiated they are assembled as an array[x-pos][y-pos] = array(RR, GG, BB, AA); of non transparent pixels.
Each object is then draw over the map by iterating its pixel array - should (and does) speed the process somewhat.
If you wanted to go astep further, I'd advise using the .gd2 file format rather than png (just slightly faster as more native to gd)
Anyway, http://www.teckis.com/testing/overlay/ovre.php uses five nested objects (images) to create the map shown....
Code: Select all
<?php
class MAPBUILD {
var $images; // array of images used
var $path; // path to images used
function MAPBUILD($img='', $colour='CCCCCC', $width=360, $height=240) {
$this->flush();
$this->path = '/home/teckisc/public_html/testing/overlay/imgs/';
$this->images['background'] = new MAKEIMAGE($this->path.$img.'.png', $colour, $width, $height);
return $this->images['background'];
}
function mergeImage($merge_img="", $x_left=0, $y_top=0, $trans_colour="0000FF", $altitude=0, $xplus=0, $yplus=0) {
if(!isset($this->images[$merge_img])) {
// image not yet created
$this->images[$merge_img] = new MAKEIMAGE($this->path.$merge_img.'.png');
$this->images[$merge_img]->tc = array(
'red'=> hexdec(substr($trans_colour,0,2)),
'green'=> hexdec(substr($trans_colour,2,2)),
'blue'=> hexdec(substr($trans_colour,4,2))
);
//
// we now have an overlay image stored in GD format as $this->images['img_name']->img
// with width $this->images['img_name']->w and height $this->images['img_name']->h
// iterate that image a pixel at a time to build array
// $this->images['img_name']->pxs[left][top] = array('red','green','blue')
//
for($this->ypo = 0; $this->ypo < $this->images[$merge_img]->h; $this->ypo++) {
for($this->xpo = 0; $this->xpo < $this->images[$merge_img]->w; $this->xpo++) {
$this->indx_rgb = imagecolorsforindex($this->images[$merge_img]->img, imagecolorat($this->images[$merge_img]->img, $this->xpo, $this->ypo));
if(
($this->indx_rgb['red'] != $this->images[$merge_img]->tc['red']) ||
($this->indx_rgb['green'] != $this->images[$merge_img]->tc['green']) ||
($this->indx_rgb['blue'] != $this->images[$merge_img]->tc['blue'])) {
// not transparent colour, so set as array index
$this->images[$merge_img]->pxs[$this->ypo][$this->xpo] = $this->indx_rgb;
}
}
}
}
$altitude = (substr($merge_img,0,1) == 'h') ? 7: 0;
if($this->images[$merge_img] == true) {
foreach($this->images[$merge_img]->pxs AS $ypos=>$vals) {
foreach($vals AS $xpos=>$pxc) {
imagesetpixel($this->images['background']->img, $x_left+$xpos+$xplus, $y_top+$ypos-$yplus-$altitude, imagecolorallocate($this->images['background']->img, $pxc['red'], $pxc['green'], $pxc['blue']));
}
}
return true;
} else {
return false;
}
}
function destroyAll() {
foreach($this->images AS $image) {
$image->destroyImage();
}
}
function showMap($savename='') {
if(strlen($savename) > 0) {
imagepng($this->images['background']->img, $savename);
} else {
header('Content-type: image/png');
imagepng($this->images['background']->img);
}
}
function flush() {
$this->images = array();
$this->path = '';
}
}
class MAKEIMAGE {
var $img; // the image itself
var $w; // width of image
var $h; // height of image
var $t; // type of image
var $tc; // rgb array of transparent colour for overlays
var $pxs; // rgba array of non transparent pixels - array[y-pos][x-pos] = rgba
function MAKEIMAGE($img='', $colour='CCCCCC', $width=32, $height=22) {
$this->flush();
$this->img = Null;
if(strlen($img) > 0 && file_exists($img)) {
// img reference sent - create from that
$this->giz = getimagesize($img);
$this->w = $this->giz[0];
$this->h = $this->giz[1];
$this->t = $this->giz[2];
$this->img = ($this->t < 4) ? ($this->t < 3) ? ($this->t < 2) ? ($this->t < 1) ? Null : imagecreatefromgif($img) : imagecreatefromjpeg($img) : imagecreatefrompng($img) : Null;
} else {
// no img reference, create blank image
$this->img = imagecreatetruecolor($width, $height);
$bgcolour = imagecolorallocate($this->img, hexdec(substr($colour,0,2)), hexdec(substr($colour,2,2)), hexdec(substr($colour,4,2)));
imagefill($this->img, 0, 0, $bgcolour);
$this->w = $width;
$this->h = $height;
}
return (is_null($this->img)) ? false : true;
}
function destroyImage() {
imagedestroy($this->img);
}
function flush() {
$this->h = 0;
$this->w = 0;
$this->img = Null;
$this->t = 3;
$this->tc = array();
$this->pxs = array();
}
}
/*********** SAMPLE UNDYNAMIC CALL */
$landscape = array(
0 => array('w1', 'w1', 'w1','w1','w1', 'w1', 'w1','g1','h1'),
1 => array(Null, 'w1', 'w1','w1','w1', 'w1', 'w1','g1','h1'),
2 => array(Null, Null, 'w1','w1','g1', 'g1', 'g1','g1','h1'),
3 => array(Null, Null, 'g1','g1','g1', 'g1', 'g1','g1','h2'),
4 => array(Null, 'g1', 'g1', 'g1', 'g1', 'g1', 'g1','g1','g1'),
5 => array('g1', 'g1', 'g1', 'g1', 'g1', 'g1', 'g1','g1','g1'),
6 => array('g1', 'g1', 'g1', Null, Null, Null, 'g1', 'g1', 'g1')
);
$rows = count($landscape);
$cols = count($landscape[0]);
$mapwidth = (($cols + $rows) * 16) + 4;
$mapheight = (($rows + $cols) * + 18;
$a = new MAPBUILD('', 'DDDDDD', $mapwidth, $mapheight);
$offsetleft = -14;
$offsettop = 10;
for($rowcnt = 0; $rowcnt < $rows; $rowcnt++) {
$offsetleft += ($cols / 2)*32;
for($colcnt = $cols-1; $colcnt > -1; $colcnt--) {
if($landscape[$rowcnt][$colcnt] != Null) {
$a->mergeImage($landscape[$rowcnt][$colcnt], $offsetleft, $offsettop);
}
$offsettop += 8;
$offsetleft -= 16;
}
$offsettop -= ($cols-1) * 8;
$offsetleft += 16;
}
$a->mergeImage('library', 146, 43);
$a->showMap();
$a->destroyAll();
// ***********************************/
?>MAPS
`id` AUTO_INCREMENT
`name` VARCHAR
`desc` VARCHAR
`rows` SMALLINT 2
`cols` SMALLINT 2
MAP_ITEMS
`id` AUTO_INCREMENT
`map_id` MEDIUMINT 8
`type` SMALLINT 2 // eg 1=terrain, 2=groundItems, 3=aerial stuff
`image` VARCHAR
`row` SMALLINT 2
`col` SMALLINT 2
`altitude` SMALLINT 2
`x_offset` SMALLINT 2
`y_offset` SMALLINT 2
then you could store the terrain and items in the same table and order by row, col, type ASC.
That would also facilitate referencing any item to a grid rather than full x,y from top left.
Feel free to hack that around any way you want.
Have a major project at work and am moving house to a non-net-connected abode, so will not have much time to domuch more to this coding. Will try to keep an eye on things during my lunchbreaks , so if you meet a brickwall, just holler and wait.