将精灵添加到 Pygame 光线投射引擎

Adding Sprites To Pygame Raycasting Engine

我目前正在使用pygame光线投射引擎pyray(https://github.com/oscr/PyRay/blob/master/pyray.py),有一些问题想请教:

1) 如何添加一个不是细胞的精灵,就像 Wolfenstein 3D 中的敌人一样?

2) 你如何制作墙壁图像,而不是色块?

  1. 纹理

    初见

    您需要的是将引擎的扫描线渲染更改为使用纹理。上面 link 中的 C++ 代码仅供初学者使用(请在代码中查找 // render scan line)并且适用于墙壁,但天花板和地板会出现变形.要补救,您需要应用 perspective correct texture mapping.

    这就是它的样子(在我将它实现到我 linked 的以前的代码之后)

    void Doom3D::draw_scanline(int sx,int sy0,int sy1,int sz0,int sz1,int symin,int tx0,int ty0,int tx1,int ty1,DWORD li)
        {
        //
        union { DWORD dd; BYTE db[4]; } cc;
        int Txs=txs*tn;
        if (sz0==sz1)   // affine texture mapping (front side of walls)
            {
            int sy,tx,ty,ktx,kty,dtx,dty,ctx,cty,dsy;
                   dsy=sy1-sy0; if (dsy<0)                        dsy=-dsy;
            ktx=0; dtx=tx1-tx0; if (dtx>0) ktx=+1; else { ktx=-1; dtx=-dtx; } tx=tx0; ctx=0;
            kty=0; dty=ty1-ty0; if (dty>0) kty=+1; else { kty=-1; dty=-dty; } ty=ty0; cty=0;
            if (dsy) for (sy=sy0;sy>=sy1;sy--)
                {
                if ((sy>=0)&&(sy<sys)&&(sy<=symin))
                 if ((tx>=0)&&(tx<Txs)&&(ty>=0)&&(ty<tys))
                    {
                    cc.dd=ptxr[ty][tx];
                    cc.db[0]=DWORD((DWORD(cc.db[0])*li)>>8);
                    cc.db[1]=DWORD((DWORD(cc.db[1])*li)>>8);
                    cc.db[2]=DWORD((DWORD(cc.db[2])*li)>>8);
                    pscr[sy][sx]=cc.dd;
                    }
                for (ctx+=dtx;ctx>=dsy;) { ctx-=dsy; tx+=ktx; }
                for (cty+=dty;cty>=dsy;) { cty-=dsy; ty+=kty; }
                }
            }
        else{           // perspective correct mapping (floor, top side of walls, ceiling)
            int sy,tx,ty,dsy,dtx,dty,_n,n,dn;
            int a,b,_z0,_z1;
            const int acc=15;
            dsy=sy1-sy0; n=abs(dsy); dn=n;
            dtx=tx1-tx0; n=abs(dtx); if (dn<n) dn=n;
            dty=ty1-ty0; n=abs(dty); if (dn<n) dn=n;
            if (sz0==0) return; _z0=(1<<acc)/sz0;
            if (sz1==0) return; _z1=(1<<acc)/sz1;
            if (dn) for (n=0;n<=dn;n++)
                {
                sy=sy0+((n*dsy)/dn);
                tx=tx0+((n*dtx)/dn);
                ty=ty0+((n*dty)/dn);
                // perspective correction (https://en.wikipedia.org/wiki/Texture_mapping)
                _n=dn-n;
                a=(_n*tx0*_z0) + (n*tx1*_z1);
                b=(_n    *_z0) + (n    *_z1); tx=a/b;
                a=(_n*ty0*_z0) + (n*ty1*_z1);
                b=(_n    *_z0) + (n    *_z1); ty=a/b;
                if ((sy>=0)&&(sy<sys)&&(sy<=symin))
                 if ((tx>=0)&&(tx<Txs)&&(ty>=0)&&(ty<tys))
                    {
                    cc.dd=ptxr[ty][tx];
                    cc.db[0]=DWORD((DWORD(cc.db[0])*li)>>8);
                    cc.db[1]=DWORD((DWORD(cc.db[1])*li)>>8);
                    cc.db[2]=DWORD((DWORD(cc.db[2])*li)>>8);
                    pscr[sy][sx]=cc.dd;
                    }
                }
            }
        }
    

    sx,sy0,sy1 是扫描线的屏幕坐标 tx0,ty0,tx1,ty1 是纹理坐标,l0,l1 是到相机的距离。 li 只是光照强度。纹理坐标是每个网格单元命中坐标的小数部分。使用的纹理是图集,因此 x 坐标还编码了从中使用的纹理。

    除此之外,您还应该添加 mip 贴图(使用接近投影单元格大小的纹理分辨率),这将避免在移动或转身时远处墙壁上的像素闪烁...

  2. 精灵

    为每个对象计算其相对于玩家的极坐标位置。所以你会有角度(相对于观察方向)和距离。您可以直接从角度计算屏幕 x,从距离计算屏幕 y0,y1。从那里只需渲染每条扫描线+/-计算位置周围的精灵宽度。您不需要精灵的透视正确纹理映射,因为它们总是垂直于玩家 ...

    需要的计算在这里:Theory behind Wolfenstein-style 3D rendering