经过差不多5个小时的努力,已经成功的将IrrLicht 1.6的部分代码整合到了古月引擎中。
去掉了IrrLicht中的以下部分:
- 音频支持
- 鼠标键盘游戏杆支持
- 各种图片加载功能
- GUI
- 两种软件渲染驱动、以及D3D8、D3D9渲染驱动
- 各种操作系统以及SDL设备驱动
目前已经在Visual C++ 2008/2010、C++Builder 2009/2010上编译链接成功。即将进行的将是IrrLicht原本例程的变更以及测试。
经过差不多5个小时的努力,已经成功的将IrrLicht 1.6的部分代码整合到了古月引擎中。
去掉了IrrLicht中的以下部分:
目前已经在Visual C++ 2008/2010、C++Builder 2009/2010上编译链接成功。即将进行的将是IrrLicht原本例程的变更以及测试。
为让《古月》的使用更为灵活,从R46版开始,支持不从GameMain入口。开发人员可以自行搭配其它引擎。同时保留原有的GameMain入口方式。
目前,新的GameMain入口方式为:
void GameMain(int,wchar_t **)
{
SystemInitInfo sii;
sii.info.ProjectName=L”Box3D”; //工程名称
sii.info.ProjectCode=L”Box3D”; //工程代码(一般使用英文名称)
sii.graphics.gui.use=false; //标记成不使用GUI
//旧式
/*
if(Init(&sii)==false) //初始化工程
return;
SetStart(new TestObject); //设定起始流程
RunApplication(); //开始运行程序
*/
//新式
GameApplication app;
if(app.Init(&sii)==false) //初始化工程
return;
app.SetStart(new TestObject); //设定起始流程
app.Run(); //开始运行程序
}
说实话,在DOS平台下置取调色板可要比在Windows平台下简单多了,而且置了之后决对正确,不会出现象DirectDraw那样置了还未必起作用的情况。
好了,言归正传,我来详细讲述一下置调色板的办法。对于读调色板就不再介绍了,因为每一个优秀的C/C++程序员都不会笨到去读取调色板。调色板是什么样的,随时都掌握在他们的程序中,要什么调色板就设置成什么样的,从来不去读调色板。
假设我们对调色板的定义如下:
uvar8 Palette[256][3];
uvar8表示8位无符号变量,表示范围是0-255。Palette变量中有256种颜色,以Palette[颜色号]来表示。每个颜色中均保存红、 绿、蓝三原色的值。Palette[0][0]就表示第0种颜色的红色值,同样Palette[0][1]就表示第0种颜色的绿色值。
在DOS平台下,设置调色板的办法是多种多样的,以下我将逐一介绍:
方法一:16位DOS平台下,调用中断进行设置
由于16位DOS平台下,主流编程工具Borland C/C++与Turbo C/C++只支持16色,所以显得很无用。所以我们只好自己调用中断进行设置了,源程序如下:void SetPalette(uvar8 pal[256][3]) { union REGS r; struct SREGS s; r.h.ah=0x10; r.h.al=0x12; r.x.bx=0; r.x.cx=256; s.es=FP_SEG(pal); r.x.dx=FP_OFF(pal); int86x(0x10,&r,&r,&s); } |
方法二:16位DOS平台下,直接写DAC进行设置
相比之下,在16位DOS平台下,置调色板最快的手法莫过于直接写DAC了,源程序如下:void SetDac(uvar8 idx,uvar8 red,uvar8 green,uvar8 blue) { asm { mov dx,3c8h mov al,idx out dx,al mov cl,2 void SetPalette(uvar8 pal[256][3]) 不要问我那倒数第二行为什么要用uvar16来定义i,而不用uvar8,您自己试一下就知道了。 |
方法三:32位DOS平台下,使用Watcom C/C++编程时直接使用其图形函数进行设置
Watcom C/C++不愧为优秀的32位DOS平台开发工具,它的图形函数直接用来编游戏都没问题,所以速度上也绝对慢不了。源程序如下:void SetPalette(uvar8 pal[256][3]) { uvar32 pal256[256]; uvar16 i; for(i=0;i<256;i++)pal256[i]=pal[i][2]/4*0x10000+pal[i][1]/4*0x100+pal[i][0]/4; _remapallpalette(&pal256); } 当然,这个程序还未做过优化,留着您自己优化吧!这里我只讲怎么设置调色板。 |
方法四:32位DOS平台下,调用中断进行设置
不过是方法一的16位转32位吧了,没什么大的变化,源程序如下:void SetPalette(uvar8 pal[256][3]) { union REGS r; struct SREGS s; r.h.ah=0x10; r.h.al=0x12; r.w.bx=0; r.w.cx=256; s.es=FP_SEG(pal); r.x.dx=FP_OFF(pal); int386x(0x10,&r,&r,&s); } |
当然,32位DOS环境也可以直接写DAC以便取得最快的速度,但是32位DOS下的汇编程序是很难写的(其实不难,只不过写出来很长),所以我就偷懒不写了,反正以上四种方法也够用了。
本来我是不准备写这篇文章的,但冷不妨有位同仁问起,也只好做个回答,顺便借此更新一下网页。
XMS,扩充内存管理规范。在16bit DOS系统下,为了使用640k以外的内存,XMS成了一种最好的手法。
在这里,我不想多讲XMS的实现,只对本文所提供的XMS类做一下使用讲解。
例子可以说明一切:
#include”XMS.H”
void main()
{
FILE *fp;
int i;
char *str;
XMS xms1(1024); //为xms1指针分配1024k的内存
fp=fopen(“C:\\WIN\\WIN386.SWP”,”rb”);
str=(char *)malloc(1024);
for(i=0;i<1024;i++)
{
fread(str,1,1024,fp);
xms1.put((void *)str,(void *)(i*1024),1024);
//将内存指针str中的1024个字节的内容写到,xms1的第i*1024个字节处
}
fclose(fp);
fp=fopen(“WIN386.SWP”,”wb”);
for(i=0;i<1024;i++)
{
xms1.get((void *)str,(void *)(i*1024),1024);
//将扩充内存xms1中,从第i*1024个字节处开始的1024个字节的内容写到str中
fwrite(str,1,1024,fp);
}
fclose(fp);
}
XMS.H
#if !defined XMS_H
#define XMS_H
class XMS {
int handle;
int move(struct EMB *emb);
public:
XMS(int size);
~XMS();
static int OK;
static int init(void);
static unsigned freesize(void);
static unsigned largestblock(void);
int realloc(int size);
int put(void *dp,void *sp,long leng);
int get(void *dp,void *sp,long leng);
};
#endif
XMS.CPP
#include <dos.h>
#include <alloc.h>
#include “xms.h”
struct EMB {
long Leng;
unsigned SourceHandle;
long SourceOfs;
unsigned DestinHandle;
long DestinOfs;
};
int XMS::OK=0;
static void far *XMSaddr;
int XMS::init(void)
{
static struct REGPACK rg;
rg.r_ax=0x4300;
intr(0x2f,&rg);
if( (rg.r_ax&0x00ff) == 0x80 )
{
rg.r_ax=0x4310;
intr(0x2f,&rg);
XMSaddr=MK_FP(rg.r_es,rg.r_bx);
OK=1;
}
else
OK=0;
return(OK);
}
unsigned XMS::freesize(void)
{
if(OK==0)
return(0);
asm {
mov ah,8
call XMSaddr
}
return _DX;
}
unsigned XMS::largestblock(void)
{
if(OK==0)
return(0);
asm {
mov ah,8
call XMSaddr
}
return _AX;
}
XMS::XMS(int size)
{
if(OK==0)
{
handle=0;
return;
}
asm {
mov ah,9
mov dx,size
call XMSaddr
}
handle=_DX;
}
XMS::~XMS()
{
if(handle==0)
return;
int hd=handle;
asm {
mov ah,0ah
mov dx,hd
call XMSaddr
}
}
int XMS::realloc(int size)
{
if(handle==0)
return(0);
int hd=handle;
asm {
mov ah,0fh
mov bx,size
mov dx,hd
call XMSaddr
}
return _AX;
}
int XMS::move(struct EMB *emb)
{
asm {
push ds
mov ah,0bh
push ds
pop es
lds si,emb
call es:XMSaddr
pop ds
}
return _AX;
}
int XMS::put(void *sp,void *dp,long leng)
{
struct EMB emb;
if(leng&1L)
leng++;
emb.Leng=leng;
emb.SourceHandle=0;
emb.SourceOfs=(long)sp;
emb.DestinHandle=handle;
emb.DestinOfs=(long)dp;
return move(&emb);
}
int XMS::get(void *dp,void *sp,long leng)
{
int v;
struct EMB emb;
if(leng&1L)
{
char *p,*d;
leng–;
if( leng>0 )
{
emb.Leng=leng;
emb.SourceHandle=handle;
emb.SourceOfs=(long)sp;
emb.DestinHandle=0;
emb.DestinOfs=(long)dp;
move(&emb);
}
p=(char *)malloc(2);
emb.Leng=2L;
emb.SourceHandle=handle;
emb.SourceOfs=(long)sp+leng;
emb.DestinHandle=0;
emb.DestinOfs=(long)p;
v=move(&emb);
d=(char*)dp;
d[leng]=p[0];
free(p);
}
else
{
emb.Leng=leng;
emb.SourceHandle=handle;
emb.SourceOfs=(long)sp;
emb.DestinHandle=0;
emb.DestinOfs=(long)dp;
v=move(&emb);
}
return(v);
}
在很多游戏中,将鼠标移到人物上,人物的边缘上就会出现一道光边。这也就是笔者将要和大家讨论的,如何找出这一边缘。
其实要找出它也不难,在游戏所使用的图片中,都包含有透明色和不透明色两部分,那么问题就很简单了,在透明色和不透明色相接的地方就是边缘。请看下面的程序:
程序功能:画指定区域图像的边缘在指定位置 |
x,y :画边缘的坐标地址 x1,y1 :指定区域左上角的坐标 x2,y2 :指定区域右下角的坐标 TColor:透明色 |
var16 i,j; uvar16 Color,TColor;for(i=y1;i<=y2;i++) for(j=x1;j<=x2;j++) { Color=GetPixel(j,i); if(Color!=TColor) //如果这个点不是透明色 { if(j==x1||GetPixel(j-1,i)==TColor)PutPixel(j-x1+x-1,i-y1+y ); if(j==x2||GetPixel(j+1,i)==TColor)PutPixel(j-x1+x+1,i-y1+y ); if(i==y1||GetPixel(j,i-1)==TColor)PutPixel(j-x1+x ,i-y1+y-1); if(i==y2||GetPixel(j,i+1)==TColor)PutPixel(j-x1+x ,i-y1+y+1); } } |
程序:罗健军
下面的算法中,var16表示有符号16位数据类型;HLine(x,y,n)是画水平线函数;VLine(x,y,n)是画垂直线函数;PutPixel(x,y)是画点函数。请看程序。
void Line(var16 x1,var16 y1,var16 x2,var16 y2)
{
var16 p,n,x,y,tn;
if(y1==y2) { if(x1>x2) { x=x2;x2=x1;x1=x; }
HLine(x1,y1,x2-x1+1);
return;
}
if(x1==x2)
{
if(y1>y2)
{
y=y2;y2=y1;y1=y;
}
VLine(x1,y1,y2-y1+1);
return;
}
if(abs(y2-y1)<=abs(x2-x1))
{
if((y2<y1&&x2<x1)||(y1<=y2&&x1>x2))
{
x=x2;y=y2;x2=x1;y2=y1;x1=x;y1=y;
}
if(y2>=y1&&x2>=x1)
{
x=x2-x1;y=y2-y1;
p=2*y;n=2*x-2*y;tn=x;
while(x1<=x2)
{
if(tn>=0)tn-=p;
else {tn+=n;y1++;}
PutPixel(x1++,y1);
}
}
else
{
x=x2-x1;y=y2-y1;
p=-2*y;n=2*x+2*y;tn=x;
while(x1<=x2)
{
if(tn>=0)tn-=p;
else {tn+=n;y1--;}
PutPixel(x1++,y1);
}
}
}
else
{
x=x1;x1=y2;y2=x;y=y1;y1=x2;x2=y;
if((y2<y1&&x2<x1)||(y1<=y2&&x1>x2))
{
x=x2;y=y2;x2=x1;y2=y1;x1=x;y1=y;
}
if(y2>=y1&&x2>=x1)
{
x=x2-x1;y=y2-y1;p=2*y;n=2*x-2*y;tn=x;
while(x1<=x2)
{
if(tn>=0)tn-=p;
else {tn+=n;y1++;}
PutPixel(y1,x1++);
}
}
else
{
x=x2-x1;y=y2-y1;p=-2*y;n=2*x+2*y;tn=x;
while(x1<=x2)
{
if(tn>=0)tn-=p;
else {tn+=n;y1--;}
PutPixel(y1,x1++);
}
}
}
}
程序:罗健军
程序中var32表示有符号32位数,var16表示有符号16位数。x0,y0为圆心坐标,r为半径。PutPixel为画点函数。
曾经长年的使用这段程序,而未向罗健军大师表示感谢,真是感到惭愧!在这里,请各位代表我向他表示感谢。
double SIN45=0.707106781186548;
void Circle(var16 x0,var16 y0,uvar16 r)
{
var32 tn;
var16 x,y;
var16 xmax;
y=r;x=0;
xmax=var16(r*SIN45);
tn=(1-r*2);
while(x<=xmax) { if(tn>=0) { tn+=(6+((x-y)<<2)); y--; } else tn+=((x<<2)+2);
PutPixel(x0+y,y0+x);
PutPixel(x0+x,y0+y);
PutPixel(x0-x,y0+y);
PutPixel(x0-y,y0+x);
PutPixel(x0-y,y0-x);
PutPixel(x0-x,y0-y);
PutPixel(x0+x,y0-y);
PutPixel(x0+y,y0-x);
x++;
}
PutPixel(x0+y,y0+x);
PutPixel(x0+x,y0+y);
PutPixel(x0-x,y0+y);
PutPixel(x0-y,y0+x);
PutPixel(x0-y,y0-x);
PutPixel(x0-x,y0-y);
PutPixel(x0+x,y0-y);
PutPixel(x0+y,y0-x);
}
程序:罗健军
void Ellipse(var16 x0,var16 y0,uvar16 r1,uvar16 r2)
{
uvar32 r,r12,r22;
var16 x,y,xmax;
var32 tn;
x=0;y=r2;
r12=r1*r1;r22=r2*r2;
xmax=var16(r12/sqrt(r12+r22));
tn=r12-2*r2*r12;
while(x<=xmax)
{
if(tn<0||y==0)tn+=(4*x+2)*r22;
else
{
tn+=(4*x+2)*r22+(1-y)*4*r12;
y--;
}
PutPixel(x0+x,y0+y);
PutPixel(x0-x,y0+y);
PutPixel(x0+x,y0-y);
PutPixel(x0-x,y0-y);
x++;
}
PutPixel(x0+x,y0+y);
PutPixel(x0-x,y0+y);
PutPixel(x0+x,y0-y);
PutPixel(x0-x,y0-y);
r=r1;r1=r2;
r2=(uvar16)r;
x=0;y=r2;
r12=r1*r1;r22=r2*r2;
xmax=var16(r12/sqrt(r12+r22));
tn=r12-2*r2*r12;
while(x<=xmax)
{
if(tn<0||y==0)tn+=(4*x+2)*r22;
else
{
tn+=(4*x+2)*r22+(1-y)*4*r12;
y--;
}
PutPixel(x0+y,y0+x);
PutPixel(x0+y,y0-x);
PutPixel(x0-y,y0+x);
PutPixel(x0-y,y0-x);
x++;
}
PutPixel(x0+y,y0+x);
PutPixel(x0+y,y0-x);
PutPixel(x0-y,y0+x);
PutPixel(x0-y,y0-x);
}
程序:罗健军
程序中var16为有符号16位数字型,uvar16为无符号16位数字型。var32为有符号32位数字型。PutPixel为画点函数。
函数Sector入口参数x0,y0为圆心坐标,r为半径。stangle为起始角度,endangle为结束角度。
double SIN45=0.707106781186548;
uvar32 SINV[91]={0,
17452406, 34899496, 52335956, 69756473, 87155742, 104528463,
121869343, 139173100, 156434465, 173648177, 190808995, 207911690,
224951054, 241921895, 258819045, 275637355, 292371704, 309016994,
325568154, 342020143, 358367949, 374606593, 390731128, 406736643,
422618261, 438371146, 453990499, 469471562, 484809620, 500000000,
515038074, 529919264, 544639035, 559192903, 573576436, 587785252,
601815023, 615661475, 629320391, 642787609, 656059028, 669130606,
681998360, 694658370, 707106781, 719339800, 731353701, 743144825,
754709580, 766044443, 777145961, 788010753, 798635510, 809016994,
819152044, 829037572, 838670567, 848048096, 857167300, 866025403,
874619707, 882947592, 891006524, 898794046, 906307787, 913545457,
920504853, 927183854, 933580426, 939692620, 945518575, 951056516,
956304755, 961261695, 965925826, 970295726, 974370064, 978147600,
981627183, 984807753, 987688340, 990268068, 992546151, 994521895,
996194698, 997564050, 998629534, 999390827, 999847695, 1000000000
};
/////////////////////////////////////////////////////////////////////////////////
// 查找表式sin函数
/////////////////////////////////////////////////////////////////////////////////
double Lsin(var16 angle)
{
double v,f;
if(angle>360||angle<-360)angle=angle-(angle/360)*360;
if(angle<0)angle=360+angle;
if(angle>180)f=-1;else f=1;
if(angle>90&&angle<=180)angle=180-angle;
else if(angle>180&&angle<=270)angle=angle-180;
else if(angle>270)angle=360-angle;
v=f*SINV[angle]/10.0e8;
return(v);
}
/////////////////////////////////////////////////////////////////////////////////
// 查找表式cos函数
/////////////////////////////////////////////////////////////////////////////////
double Lcos(var16 angle)
{
double v,f;
if(angle>360||angle<-360)angle=angle-(angle/360)*360;
if(angle<0)angle=360+angle;
if(angle<270&&angle>90)f=-1;else f=1;
if(angle>90&&angle<=180)angle=180-angle;
else if(angle>180&&angle<=270)angle=angle-180;
else if(angle>270)angle=360-angle;
angle=90-angle;
v=f*SINV[angle]/10.0e8;
return(v);
}
/////////////////////////////////////////////////////////////////////////////////
// 扇形绘制函数
////////////////////////////////////////////////////////////////////////////////
void Sector(var16 x0,var16 y0,uvar16 r,uvar16 stangle,uvar16 endangle)
{
var16 i,j;
var16 *xy;
var16 bx,ex,bxd,exd,bxf,exf,ben;
var32 tn,x,y;
var32 xmax;
y=r; x=0;
xmax=(var32)(r*SIN45);
tn=(1-r*2);
xy=(var16 *)calloc(20,sizeof(var16));
xy[ 0]=x0+r;xy[1]=y0;
xy[ 2]=x0; xy[3]=y0-r;
xy[ 4]=x0; xy[5]=y0-r;
xy[ 6]=x0-r;xy[7]=y0;
xy[ 8]=x0-r;xy[9]=y0;
xy[10]=x0; xy[11]=y0+r;
xy[12]=x0; xy[13]=y0+r;
xy[14]=x0+r;xy[15]=y0;
bx=stangle/45;
ex=endangle/45;
ben=ex-bx-1;
xy[16]=(var16)(r*Lcos(stangle));
xy[17]=(var16)(r*Lsin(stangle));
xy[18]=(var16)(r*Lcos(endangle));
xy[19]=(var16)(r*Lsin(endangle));
Line(x0+xy[16],y0-xy[17],x0,y0);
Line(x0+xy[18],y0-xy[19],x0,y0);
if(bx==1||bx==2||bx==5||bx==6)bxd=abs(xy[16]);else bxd=abs(xy[17]);
if(ex==1||ex==2||ex==5||ex==6)exd=abs(xy[18]);else exd=abs(xy[19]);
if(bx==0||bx==2||bx==4||bx==6)bxf=0; else bxf=1;
if(ex==0||ex==2||ex==4||ex==6)exf=1; else exf=0;
while(x<=xmax)
{
if(tn>=0)
{
tn+=(6+((x-y)*4));
y--;
xy[0]--;
xy[3]++;
xy[5]++;
xy[6]++;
xy[8]++;
xy[11]--;
xy[13]--;
xy[14]--;
}
else tn+=((x*4)+2);
if(stangle<endangle)
{
j=(bx+1)*2;
for(i=0;i<ben;i++)
{
PutPixel(xy[j],xy[j+1]);
j+=2;
}
}
else if(stangle>endangle)
{
j=(bx+1)*2;
for(i=bx+1;i<8;i++)
{
PutPixel(xy[j],xy[j+1]);
j+=2;
}
j=0;
for(i=0;i<ex;i++)
{
PutPixel(xy[j],xy[j+1]);
j+=2;
}
}
i=bx*2;
if( (x>bxd)^bxf )PutPixel(xy[i],xy[i+1]);i=ex*2;
if( (x>exd)^exf )PutPixel(xy[i],xy[i+1]);x++;
xy[ 1]--;
xy[ 2]++;
xy[ 4]--;
xy[ 7]--;
xy[ 9]++;
xy[10]--;
xy[12]++;
xy[15]++;
}
free(xy);
}
首先,在解析几何中我们可以找到以下公式:
x’=x*cos(ang)-y*sin(ang) y’=x*sin(ang)+y*cos(ang) |
这个公式是以原点(0,0)为中心,将点(x,y)旋转ang度,旋转后的坐标为(x’,y’)。不过这只是以原点为中心进行旋转的,如果我们想以任意点为中心旋转呢!公式变形如下:
x’=zx+(x-zx)*cos(ang)-(y-zy)*sin(ang) y’=zy+(x-zx)*sin(ang)+(y-zy)*cos(ang) |
好,这也就是我们想要的结果了。以(zx,zy)为中心,将点(x,y)旋转ang度,旋转后的坐标为(x’,y’)。
由于在标准C/C++库中,sin和cos所要求的参数为弧度,而并非角度,所以我们需要一个转换。
弧度=角度*圆周率/180 |
按以上,最后的标准C/C++程序如下:
float x,y; //原始点坐标 float rx,ry; //旋转中心点坐标 float nx,ny; //旋转后的点坐标 float ang; //旋转角度(0-360) float as,ac;…… as=sin(ang*M_PI/180.0); nx=rx+((x-rx)*ac-(y-ry)*as); |