游戏编程二:精灵的处理

游戏编程二:精灵的处理
 
    先前我们介绍了我们小组对于位图的处理,现在我们将介绍我们对于精灵类的处理。
    我们把一些贴图零件统称为精灵,对于一个游戏场景来说,就是在地图上贴上不同的精灵,包括物体object,角色player,敌人enemy,其实一个精灵就是一个位图,结合先前我们对位图的处理,就可以很方便的处理场景。
    首先我们定义一个精灵base类,以后所有的精灵将继承该类,由于对于可以移动的精灵,以角色为例,我们参考RPG Maker里面的做法,人物在四个方向上都有停--左脚--停--右脚四个状态,依次调用四个状态就可以让人物移动,而在程序里的做法就是通过bitblt函数不断改变ox,oy里的值。
    精灵类CSprite的定义为下:
#ifndef     __sprite_h__
#define     __sprite_h__
 
class CDibSection;
class CSprite {
 public:
       CSprite();
       CSprite(CDibSection *dib, CPoint pos, CSize _size, int d=0);
       CSprite(CDibSection *dib, CPoint pos, CPoint src, CSize _size, int d=0);
       virtual ~CSprite();
 
       void Set(CDibSection *dib, CPoint pos, CSize _size, int d=0);
       void Set(CDibSection *dib, CPoint pos, CPoint src, CSize _size, int d=0);
       void Draw(CDibSection &image);
       void Draw(CDibSection &image, const CRect &rect);
       void GetRect(CRect *r) const;
       void SetDrawPos(int x, int y);
       void SetDrawPos(CPoint point);
       CPoint GetDrawPos() const;
       void SetSrcPos(int x, int y);
       int GetDepth() const;
       void SetDepth(int d);
       BOOL PtIn(POINT pt);
 
 protected:
       CPoint     draw_pos;                     //绘制位置
       CPoint     src_pos;                      //CG显示位置的坐标
       CSize      size;                         //显示尺寸的大小
       CDibSection *dib;                     //指向CG数据的指针
       int           depth;                        //sprite层次0, 1, 2.......
} ;
首先,draw_pos是将把精灵绘制到map的位置,我们小组的处理不是直接将精灵绘制到client表面,而是首先将其绘制到缓存里的背景上,在绘制到表面,所以draw_pos是相对于背景的坐标,而src_pos则是cg坐标,通过该坐标来定位需要绘制的精灵,因为我们为了方便,通常将一个精灵的不同状态做成一张图,这样就方便管理,至于绘图时就通过src_pos来定位,具体参见bitblt函数说明,而size就是需要显示的尺寸大小,对于一个player来说,我们通常会用一个4*4的图片来表示他的移动状态。
所以他的尺寸大小为图片的宽度与高度除以4。
在sprite里,我们不把位图放进去,只是用了一个CDibSection *dib指针,位图的处理我们将放在另一个类里处理,这里不再说明。
对于depth,我们设定的是精灵的层次,在一个场景里,精灵的显示是有顺序的,精灵的前后关系很重要,不然在重迭时可能会出现在前面的精灵跑到后面的情况,这里我们先做一个约定,我们会将整个地图分成格子,每个格子为32*32像素。
这样在显示精灵时,我们约定,对于同一个格子上的,层次越低的,越先绘制,对于同一个y轴上的,y坐标越小得越先绘制。这样精灵重叠时就不会发生错误。
下面是函数的具体实现:
//
//sprite设置
//
inline void CSprite::Set(CDibSection *_dib, CPoint pos, CSize _size, int d)
{
       draw_pos = pos;
       size = _size;
       dib = _dib;
       depth = d;
}
 
inline void CSprite::Set(CDibSection *_dib, CPoint pos, CPoint src, CSize _size, int d)
{
       draw_pos = pos;
       src_pos = src;
       size = _size;
       dib = _dib;
       depth = d;
}
 
//
//sprite CG显示位置坐标设定
//
inline void CSprite::SetSrcPos(int x, int y)
{
       src_pos.x = x;
       src_pos.y = y;
}
 
 
//
//sprite 绘制位置坐标设定
//
inline void CSprite::SetDrawPos(int x, int y)
{
       draw_pos.x = x;
       draw_pos.y = y;
}
 
inline void CSprite::SetDrawPos(CPoint point)
{
       draw_pos = point;
}
 
//
//取得绘制坐标
//
inline CPoint CSprite::GetDrawPos() const
{
       return draw_pos;
}
 
//
//取得层次
//
inline int CSprite::GetDepth() const
{
       return depth;
}
 
//
//设置层次
//
inline void CSprite::SetDepth(int d)
{
       depth = d;
}
 
//
//得到绘制位置区域
//
inline void CSprite::GetRect(CRect *r) const
{
       r->left = draw_pos.x;
       r->top = draw_pos.y;
       r->right = draw_pos.x + size.cx;
       r->bottom = draw_pos.y + size.cy;
}
 
 
inline BOOL CSprite::PtIn(POINT pt)
{
       return CRect(draw_pos, size).PtInRect(pt);
}
 
//精灵混合
void CSprite::Draw(CDibSection &image)
{
       image.Mix(*dib, draw_pos, size, src_pos, RGB(255, 255, 255));
}
 
 
void CSprite::Draw(CDibSection &image, const CRect &rect)
{
       CRect r(draw_pos, size);
       r &= rect;
       if (!r.IsRectEmpty()) {
              CPoint     src = src_pos;
              CPoint     dest = draw_pos;
              if (dest.x < r.left) {
                     src.x += r.left - dest.x;
                     dest.x = r.left;
              }
              if (dest.y < r.top) {
                     src.y += r.top - dest.y;
                     dest.y = r.top;
              }
              image.Mix(*dib, dest, r.Size(), src);
       }
}
这里面重点关注的是set函数,它将设定精灵的显示位置,以及贴图所需的图片,而对于图像,则是通过指针操纵。
所以操纵精灵时,我们先用set函数将其设定到指定位置,在用draw函数与背景贴图重叠,这样就可以了。
精灵包括物体,敌人,还有角色,而在某一方面来说,敌人与角色也可以算得上物体,所以我们先写一个CMapSprite类,继承自CSprite
class CMapSprite: public CSprite
{
public:
      
 
public:
       CMapSprite();
       CMapSprite(CDibSection *dib, CPoint pos, CSize size, int d);
 
       void SetMapPoint(CPoint point);
       CPoint GetMapPoint() const { return map_point; }
       void SetMapIndex(CPoint point);
       CPoint GetMapIndex() const {return map_index; }
 
       bool operator==(CPoint p);
       bool operator<(const CMapSprite &x) const;
 
protected:
       CPoint map_point;          //地图坐标
       CPoint map_index;          //地图索引坐标
};
 
在这里,map_point表示的是地图坐标,用来表明该精灵相对于背景地图的坐标,而map_index则是地图索引坐标,我们先前说过,将地图分成了32*32的格子,我们就用map_index表明该精灵所在的格子坐标,当然地图坐标和索引坐标可以互相转换,函数如下
enum       {
       MAPGRID_WIDTH = 32,
       MAPGRID_HEIGHT = 32,
} ;
 
inline CPoint PointToIndex(int x, int y)
{
       return CPoint(x / MAPGRID_WIDTH, y / MAPGRID_WIDTH);
}
 
inline CPoint PointToIndex(CPoint point)
{
       return PointToIndex(point.x, point.y);
}
 
inline CPoint IndexToPoint(int x, int y)
{
       return CPoint(x * MAPGRID_WIDTH, y * MAPGRID_HEIGHT);
}
 
inline CPoint IndexToPoint(CPoint point)
{
       return IndexToPoint(point.x, point.y);
}
 
而对于CMapSprite类的函数实现,如下
//设置地图坐标
void CMapSprite::SetMapPoint(CPoint point)
{
       map_point = point;
       SetDrawPos(IndexToPoint(point) + CPoint((MAPGRID_WIDTH - size.cx) / 2, MAPGRID_HEIGHT - size.cy));
}
 
 
//设置地图索引
void CMapSprite::SetMapIndex(CPoint point)
{
       map_index = PointToIndex(point - CPoint((MAPGRID_WIDTH - size.cx) / 2, MAPGRID_HEIGHT - size.cy));
}
 
bool CMapSprite::operator==(CPoint p)
{
       return map_point.x == p.x && map_point.y == p.y;
}
 
// 为了要决定显示顺序,要比较大小关系和坐标
//
bool CMapSprite::operator<(const CMapSprite &x) const
{
       if (draw_pos.y + size.cy == x.draw_pos.y + x.size.cy) {
              if (draw_pos.x == x.draw_pos.x) {
                     return depth < x.depth;
              }
              return draw_pos.x < x.draw_pos.x;
       }
       return draw_pos.y + size.cy < x.draw_pos.y + x.size.cy;
}
 
在这里,由于精灵可能不是32*32一个格子大,所以我们在绘制的时候,还要经过一些处理。
 
人物的的绘制坐标其实应为人物的理论地图坐标加上CPoint((MAPGRID_WIDTH - size.cx) / 2, MAPGRID_HEIGHT - size.cy),而人物的索引坐标则是人物实际坐标减去CPoint((MAPGRID_WIDTH - size.cx) / 2, MAPGRID_HEIGHT - size.cy)在转换得来的。
然后我们再写一个CCharacter类继承自CMapSprite,该类实现敌人与角色,其实敌人与角色就可以看成会动的物体,而且多了一些属性罢了,但是在该类中,由于我们小组还没实现战斗系统,所以属性没有定义,以后会慢慢说明。
class CCharacter: public CMapSprite
{
public:
       CCharacter(CDibSection *dib, CPoint pos, CSize size, int d);
 
       bool ClearStatus();
 
       bool operator==(const CCharacter &x);
 
public:
       CString name; // 名称
};
是函数实现如下:
bool CCharacter::operator==(const CCharacter &x)
{
       return name == x.name;
}
这个类很简单,只是因为我们还没有添加一些属性,具体实现等以后再说。
 
 
 

你可能感兴趣的:(游戏编程,游戏,编程,image,class,object)