| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 2182 人关注过本帖
标题:[转帖]isometric 斜视角图形引擎介绍
只看楼主 加入收藏
ant3000
Rank: 1
等 级:新手上路
帖 子:188
专家分:0
注 册:2004-6-7
收藏
 问题点数:0 回复次数:3 
[转帖]isometric 斜视角图形引擎介绍
   为了帮助理解斜视角图形系统(isometric view),首先我们要复习一下砖块式图形
系统(tiled graphics)。砖块图素大多是由一个矩形区域的象素集合所组成的。顾名思
义,砖块式图形系统就象组成地板的瓷砖,一块一块紧密的排列在一起。N个小砖块排列
在一起又组成一个图案。
  砖块式图形引擎里,为了渲染一个场景,通常采用数组形式存储着砖块图素。经典的
渲染方法是从左上角开始,向右边绘制,到达最右端后,再从下一行的最左端开始。如此
反复。
  斜视角图形系统,可以理解为用一种旋转视角绘制砖块图形场景的系统。X轴沿右下
方向递增,Y 轴沿左下方向递增。
如图:
砖块图形坐标:              斜视角图形坐标:      
- X -                           0 0
    0123456789             /  1  *  1   \
  0 **********           Y  2  *   *  2   X
| 1 *    **  *         /  3  *       *  3   \
Y 2 *  ****  *          4  *           *  4
| 3 *  **    *        5  *               *  5
  4 *        *         *                   *  6
  5 **********           *          *    *   *  7
                           *      *   *    *   *  8
                             *      *   *        *  9
                               *                   *
                                 *               *
                                   *           *
                                     *       *
                                       *   *
                                         *
因为我们的显示器是方的(废话:),所以在斜视角图形系统中你将看见如下的图形
------------------------
|  \    草地    /      |
|    \        /        |
|      \    /          |  (倾斜视角后的图形)
|        \/     湖泊   |
|  沙漠    \           |
|            \         |
------------------------
现在我们已经粗略的解释了什么是斜视角图形系统,组成斜视角图形系统的图素(tile)
存在着 长度(height),宽度(width)和 深度(depth)这几个要素,其中长度和宽度
影响了斜视角图形系统的视角,而深度决定了图素间的层次关系。
作者认为斜视角图形系统采用 2:1 的横纵比是较合理的,我们将在下面的介绍中发现它
的优点。精确的说我采用的是 2.1:1的斜率,我们的宽度选择32个像素,那么长度就等
于 32/2.1=15.23,四舍五入后就成了: 32 X 15。如下图:
                      1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 3
    1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2
   ----------------------------------------------------------------
 1|                             O O O O
 2|                         O O O O O O O O
 3|                     O O O O O O O O O O O O
 4|                 O O O O O O O O O O O O O O O O
 5|             O O O O O O O O O O O O O O O O O O O O
 6|         O O O O O O O O O O O O O O O O O O O O O O O O
 7|     O O O O O O O O O O O O O O O O O O O O O O O O O O O O
 8| O O O O O O O O O O O O O O O O O O O O O O O O O O O O O O O O
 9|     O O O O O O O O O O O O O O O O O O O O O O O O O O O O
10|         O O O O O O O O O O O O O O O O O O O O O O O O
11|             O O O O O O O O O O O O O O O O O O O O
12|                 O O O O O O O O O O O O O O O O
13|                     O O O O O O O O O O O O
14|                         O O O O O O O O
15|                             O O O O
  如果你仔细观察了上图,会发现32 X 15尺寸的图素可以很紧密的接合在一起只要向
右移动16个象素,向下移动8个象素就得到了下一个图素的启始位置。因此我们渲染好
的画面如下:
+-----------+
|/\/\       |
|\/\/\      |
|/\/\/\     |
|\/\/\/\    |
|/\/\/\/\   |
|\/\/\/\/\  |
|/\/\/\/\/\ |
+-----------+
这时有人会问了,按什么顺序画tile呢?请再看下面一张我们进行放大,并且加入坐标
的图例:
  +-------------------+
  |   /\              |
  | /  0 \            |
  |/\  0 /\           |          Note:
  | 0 \/  1 \         |
  | 1 /\  0 /\        |              /      \
  |\/  1 \/  2 \      |             Y   /\   X
  | \  1 /\  0 /\     |            /   /\/\   \
  | 1 \/  2 \/  3 \   |               /\/\/\
  | 2/ \  1 /\  0 / \ |               \/\/\/
  +-------------------+
我把它正过来,其坐标位置就想这样:
  -X-
| 0 1 2 3
Y 1 X X X
| 2 X X X
  3 X X X
从图上看,好象从左到右,从上到下的绘制屏幕是顺理成章的事。其实不然,这不是最好
的算法,我们准备采用从左到右,“从上到下”的方法,啊?!有的朋友肯定会说这不是
和上面的方法一样吗,不一样(没看见引号吗 ;)。
  我来解释一下,其实从左到右是绝对正确的,而且彼此两列图素之间间隔32个象素,
但是从上到下画的时候请注意:彼此两行图素间隔不是15个象素,而是8个象素,而且
相邻的两行图素的横坐标相差16个象素,比如第0行的图素用0,0开始画起,那么第1
行的图素就应该从16,8开始画,同理第2行的图素就应该是从0,16坐标画。请结合上面
的图例再想想就清楚了 : )
  因此我画图的顺序应该是:
  +-----------------+
  |0  /\ 1  /\ 2  /\|3
  | /    \/    \/   |
 4|/\ 5  /\ 6  /\ 7 | 8
  |   \/    \/    \/|
  |9  /\ 10 /\ 11 /\|12
  |\/    \/    \/   |
13|/\14  /\15  /\16 |17
  |   \/    \/    \/|
  |18 /\19  /\20  / |21
  +-----------------+
大家也许会注意到:上图中每一行都在左边多画了一个只能看见一半的图素,如果不这做
的话,大家能想象的出来:在屏幕的边缘会出现一条丑陋的锯齿状黑边。多画一个图素就
可以补偿,对绘图速度没有太大的损失,所以我们采用这种办法。
  是不是有些了解了? 那么如何在程序中实现这些具体步骤呢? OK, 我们在仔细观察拿
幅有坐标的图例看看:
  +-------------------+
  |   /\              |
  | /  0 \            |
  |/\  0 /\           |          Note:
  | 0 \/  1 \         |
  | 1 /\  0 /\        |              /      \
  |\/  1 \/  2 \      |             Y   /\   X
  | \  1 /\  0 /\     |            /   /\/\   \
  | 1 \/  2 \/  3 \   |               /\/\/\
  | 2/ \  1 /\  0 / \ |               \/\/\/
  +-------------------+
会发现:当我们在水平方向移动时,X坐标增加了1,而Y坐标却减少了1.我再来看看垂直方
向移动时坐标又是如何变化的:
   \                   0,0
    \     MAP CORDS:      1,0      <---- increase x
   /                   1,1         <-- increase y
    \                     2,1      <---- increase x
   /                   2,2         <-- increase y
这和水平方向移动大不相同了,好象我们仅仅需要把X,Y不停的加1,就能实现垂直移动了.
1. 如果是奇数行,我只要X=X+1
2. 如果是偶数行,我只要Y=Y+1
哈哈,就这么简单!现在我们已经学会了如何渲染屏幕和如何跟踪坐标.不难吧 :-)下面我
们要来啃硬骨头了:在程序中实现斜视角图形引擎.
首先选择一个你擅长的数据结构来存储Map( 定长数组,可变数组,链表 ).我们的例子程序
将使用 10x10 的定长数组,并且针对一个区域具有层叠图素的能力,来实现有深度效果的
Map.
  举个例子:如果你想在你的Map上实现一堵墙和一堵挂着蜡烛的墙,只要画一个墙和一
支蜡烛,然后在Map上同一个地方定义墙和蜡烛,那么就能得到层叠效果.而且你的蜡烛还
可以和别的物体进行组合.
所以我们的Map上一个"点"数据将定义成如下结构:
struct MAP_STRUCTURE {
    char num_tiles;  //最多十个图素
    char tiles[10];  //每个图素的索引值
    char height[10]; //每个图素的深度值
  };
我们的Map定义如下:
MAP_STRUCTURE map[10][10]; //10x10的Map
如果我们有以下几种物体在Map上:
  0) 草地
  1) 墙
  2) 高墙
看一下我们的地图
    0 1 2 3 4 5 6 7 8 9
  0 O O O O O O O O O O
  1 O . . . . . . . . O
  2 O . . . . . . . . O           . = 草地       (0)
  3 O . . o o o o . . O           o = 墙         (1)
  4 O . . o . . o . . O           O = 高墙       (2)
  5 O . . o . . o . . O
  6 O . . o o o o . . O
  7 O . . . . . . . . O
  8 O . . . . . . . . O
  9 O O O O O O O O O O
如何实现草地,墙,高墙呢,首先最容易实现的是"草地",只要画一个16x15的草地图案就可
以,我将用16x50的尺寸来画"墙",那么"高墙"就可以通过我们前面讲的层叠方法.用两堵"
墙"来实现一堵"高墙",第二堵"墙"的高度应该是第一堵"墙"的两倍.
  map[0][0].num = 2;
  map[0][0].tile[0] = 1;
  map[0][0].height[0] = 0;
  map[0][0].tile[1] = 1;
  map[0][0].height[1] = 50;
  ...
  map[1][1].num = 1;
  map[1][1].tile[0] = 0;
  map[1][1].height[0] = 0;
...
你可以想象的出来,在我们的绘图代码里,只要循环的绘制每一层的图素就可以了.由于每
个图素都有自己高度和宽度,所以我必须要选择一个绘图的起点,来满足绘制不同尺寸的图
素.本文作者认为最右下角的点坐标是最理想的起点.只要减去图素的高度和宽度就能到对
应屏幕上的起点.
我们可以用C实现:
(注意:这并不是一个"斜视角"的绘制方法,仅仅是为了让大家理解层叠的绘制方法)  
  for(i=0;i<10;i++) {
    for(j=0;j<10;j++) {
      for(k=0;k<map[i][j].num;k++) {
        tile_to_draw = map[i][j].tile[k];
        height_to_draw = map[i][j].tile[k];
        width = block_width(tile_to_draw);
        height = block_height(tile_to_draw);
        block_draw(tile_to_draw,x-width,y-height-height_to_draw);
      }
    }
  }
你不得不从上面的例子里适应这个坐标系统,这样有助于你理解"斜视角"的绘图方法.当我
们从自上而下,从左到右的绘制时当前图素将自动覆盖上一列甚至更远列的图素,所以我们
不需要用到象Z-Buffer那样的方法实现消隐.
为了实现"斜视角"图形系统,我们必须遵循下面的原则:
1. 启始坐标应该从(8,16)开始(该图素只能看见下半部分),在绘制图素前必须记得先减去
图素的高度和宽度
2. 我们用循环方式画每一列,一个 320x200 的屏幕最多能画25列,而实际上我们需要绘
制更多列,为了不裁剪掉上面几列较高的图素.因此我们选择画35列.
3. 在绘制屏幕时,我们必须建立一个坐标系,对应屏幕坐标.首先我们的循环判断是否是奇
数行,如果是则该行右移16个象素,接着在循环绘制该行的图素.最后Y坐标加1,如此循环
反复.在320x200的屏幕上水平横向有12个可视图素,其中有两个只能看见一半.垂直纵向
有13个可视图素.
4. 画图素利用上面我们已经讲过的结构.当右移32个象素时,意味着我们的相对坐标系
X+1,Y-1.完成水平一行的绘制后,Y+8移动到下一列,如果是奇数行则X+1,如果是偶数行则
Y+1,如此循环就得到了整个MAP.当按照上述方法完成你的引擎,你会发现,不管向哪个方向
移动精灵,都是要么"跳"32个象素,要么"跳"16个象素.显示非常不平滑.其实我们只要加
一个小小的改动:
首先我们仍然看看10x10的Map:
    -X-
      0123456789
  | 0 ..........
  Y 1 ..........
  | 2 ..........
    3 ..........
    4 ..........
    5 ..........
    6 ..........
    7 ..........
    8 ..........
    9 ..........
现在,我们把每一个图素定义成16x16的一个相对坐标系:
        X0                X1
    ................ ................
    ................ ................
    ................ ................
    ................ ................
    ................ ................
    ................ ................
  Y ................ ................
  0 ................ ................
    ................ ................
    ................ ................
    ................ ................
    ................ ................
    ................ ................
    ................ ................
    ................ ................
    ................ ................
这样一来,Map仍然是10X10个图素,但是我们的精灵可以在(10x16)x(10x16)=(160x160)的
相对坐标里任意移动.我们这种坐标系统叫:精密坐标系统.
下面将要对我们的引擎做一些改动,以适应精密坐标系统
1. 我们定义变量:vx,vy表示160x160的坐标系统
2. vx,xy除以16就得到了实际的图素坐标:mx,my
mx=vx/16;my=vy/16
3. 我们还要定义几个变量:prestep_x,prestep_y,x_off,y_off都表示了偏移量,其中
x_off,y_off表示了相对Map坐标了偏移,prestep_x,prestep_y表示了相对屏幕坐标的偏
移.
其中的转换公式如下:
   x_off = vx and 15
   y_off = vy and 15
       prestep_x = x_off - y_off
       prestep_y = (x_off / 2) + (y_off / 2)
       (这些函数公式是基于32x15的尺寸)
上面的方法介绍了在精密坐标系统中如何得出相对屏幕的偏移坐标.在原来绘制图素的坐
标上在相应的加上偏移量就可以得到图素偏移后的新位置.
到现在我们还剩最后一个问题了,就是精灵应该什么时候显示在 Map上,如果在所有Map场
景绘制完再显示,精灵会遮挡住某些不应该被遮挡的物体.所以我们只有在绘制Map场景时
把精灵也加进去,这样才能得到正确的画面.
  因此我们还要引入图层(Layer)这个概念.有了图层我们就能顺利的解决了绘制的顺
序.
下面我们将定义一个绘制的顺序(针对Map上一个单位):
1. 首先要处理的草地等那些紧帖地面的图素.
2. 其次是是物体,请注意大物体如:高墙虽然有两堵墙组成但是我们把他作为一个物体放
在同一层.
如果你的引擎按这样的顺序的组织,我们将得到正确的视觉效果.并不需要用Zbuffer来处
理.
这样一来我要对原来定义的数据结构做一些调整:
struct MAP_STRUCTURE {
    char num_tiles;
char tiles[10];  
char height[10];
    char layer[10];  // 图层
  };
  MAP_STRUCTURE map[10][10];
  初始化...
  map[0][0].layer[0] = 1;
  map[0][0].layer[1] = 1;
  ...
  map[1][1].layer[0] = 0;
  ...
  以下是绘图函数的修改:
  current_layer = 0;
  max_layers    = 0;
  while(1) {
    for(i=0;i<10;i++) {
      for(j=0;j<10;j++) {
        for(k=0;k<map[i][j].num;k++) {
          if(map[i][j].layer == current_layer) {
            // draw the tile
          }
          if(map[i][j].layer > max_layers)
            max_layers = map[i][j].layer;
        }
      }
    }
    current_layer++;
    if(current_layer >= max_layers)
      break;
  }
最后我们要讨论一下在绘制Map时怎样把精灵也绘制上去.其实我们只要在绘制Map时判
断一下精灵是否在当前Map位置上:
(sprite_x,sprite_y是"精密坐标系")
if(mx == sprite_x / 16  &&  my == sprite_y / 16) {
然后我们根据精灵在Map的偏移量绘制就可以了:
  xo = sprite_x & 15;
  yo = sprite_y & 15;
  xx   = xo - yo;
  yy   = (xo/2) + (yo/2);
  block_draw(sprite_num,screenx-32+xx,screeny-16+yy);
搜索更多相关主题的帖子: 砖块 isometric 图形引擎 视角 转帖 
2004-06-24 12:01
chengstone
Rank: 16Rank: 16Rank: 16Rank: 16
等 级:版主
帖 子:562
专家分:226
注 册:2004-4-3
收藏
得分:0 
一个字 晕

qq:69558139
2004-07-04 13:59
pwlsy
Rank: 1
等 级:新手上路
帖 子:38
专家分:0
注 册:2004-12-3
收藏
得分:0 

ant3000什么时候好好聊聊啊!

感觉真是有点相见恨晚啊!


荆轲,这才是刺客,绝对不同于杀手,杀手是不可能被载入史册的。风萧萧兮易水寒,壮士一去兮不复还,这是我最向往的感觉,有些无奈,有些凄凉,但更多的是大丈夫一去不回头的气势。
2004-12-11 00:34
kaikai
Rank: 1
等 级:新手上路
帖 子:236
专家分:0
注 册:2005-1-7
收藏
得分:0 
其实斜视角不必用'斜'的图。只要逻辑上是斜的格子就行了。图片仍然可以使用方的。

Have you visit acm.tongji. lately?
2005-01-07 18:59
快速回复:[转帖]isometric 斜视角图形引擎介绍
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.082234 second(s), 7 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved