2017-11-11 9 views
1

私は、「ウィンドウの迷路」を作成し、レイキャスティングアルゴリズムを使用するJavaプロジェクトを持っています。ここではスクリーンショットです:異なる高さのサイズを持つレイキャスティング

Maze made with ray casting

あなたはすべての壁が同じ高さのサイズを持つ見ることができますのように。私は、色、距離と光の通常のWALL_HEIGHT使用するのと同じしかし

private void castRay(int xOnScreen,double angle,double direction) { 
    R rx = castRayInX(angle,direction); 
    R ry = castRayInY(angle,direction); 
    // In case of out-of-space rays 
    if (rx.getDistance()==Double.MAX_VALUE && ry.getDistance()==Double.MAX_VALUE) { 
     graphics.setColor(BACKGROUND); 
     graphics.drawLine(xOnScreen,0,xOnScreen,this.image.getHeight()); 
     return; 
    } 
    double distance = rx.getDistance(); 
    double normal = rx.getNormal(); 
    Color c = rx.getColor(); 
    double coef = Math.cos((angle+direction+Math.PI)-normal); 
    Plot collision = rx.getPlot(); 

    if (ry.getDistance()<rx.getDistance()) { 
     distance = ry.getDistance(); 
     normal = ry.getNormal(); 
     c = ry.getColor(); 
     coef = Math.cos((angle+direction+Math.PI)-normal); 
     collision = ry.getPlot(); 
    } 

    coef = Math.abs(coef); 
    int factor = map.length*SQUARE_SIZE; 
    double d = (double)(distance+factor)/factor; 
    coef *= 1/(d*d); 
    Color c2 = new Color((int)(c.getRed()*coef),(int)(c.getGreen()*coef),(int)(c.getBlue()*coef)); 
    graphics.setColor(c2); 
// graphics.setColor(c); // no illumination 
    distance *= Math.cos(angle); // lens correction 
    int h = (int)(this.screenDistance/distance*WALL_HEIGHT); // perspective height 
    int vh = this.image.getHeight(); 
    graphics.drawLine(xOnScreen,(vh-h)/2,xOnScreen,(vh+h)/2); 
    drawEye(direction,collision); 
} 

private R castRayInX(double angleRay,double direction) { 
    double angle = angleRay+direction; 
    double x1 = eye.getX()+SQUARE_SIZE*Math.cos(angle); 
    double y1 = eye.getY()+SQUARE_SIZE*Math.sin(angle); 
    double slope = (y1-eye.getY())/(x1-eye.getX()); 
    if (Math.cos(angle)==0) { 
     if (Math.sin(angle)>0) 
      return new R(Double.MAX_VALUE,3*Math.PI/2,BACKGROUND,null); 
     else 
      return new R(Double.MAX_VALUE,Math.PI/2,BACKGROUND,null); 
    } 
    if (Math.cos(angle)>0) { 
     int firstX = ((eye.getX()/SQUARE_SIZE)+1)*SQUARE_SIZE; 
     R r = new R(Double.MAX_VALUE,angle+Math.PI,BACKGROUND,null); 
     for (int x = firstX; x<map[0].length*SQUARE_SIZE; x += SQUARE_SIZE) { 
      int y = (int)(slope*(x-eye.getX())+eye.getY()); 
      if (isOutside(x,y,Color.MAGENTA,this.showRayCastingX)) break; 
      Color c = colorAt(x,y); 
      if (c==null) c = colorAt(x,y-1); 
      if (c==null) c = colorAt(x-1,y); 
      if (c==null) c = colorAt(x-1,y-1); 
      if (c!=null) { 
       int DX = x-eye.getX(); 
       double DY = y-eye.getY(); 
       return new R(Math.sqrt(DX*DX+DY*DY),Math.PI,c,new Plot((int)x,(int)y, WALL_HEIGHT)); 
      } 
     } 
     return r; 
    } else { 
     int firstX = ((eye.getX()/SQUARE_SIZE))*SQUARE_SIZE; 
     R r = new R(Double.MAX_VALUE,angle+Math.PI,BACKGROUND,null); 
     for (int x = firstX; x>=0; x -= SQUARE_SIZE) { 
      int y = (int)(slope*(x-eye.getX())+eye.getY()); 
      if (isOutside(x,y,Color.MAGENTA,this.showRayCastingX)) break; 
      Color c = colorAt(x,y); 
      if (c==null) c = colorAt(x,y-1); 
      if (c==null) c = colorAt(x-1,y); 
      if (c==null) c = colorAt(x-1,y-1); 
      if (c!=null) { 
       int DX = x-eye.getX(); 
       double DY = y-eye.getY(); 
       return new R(Math.sqrt(DX*DX+DY*DY),0,c,new Plot((int)x,(int)y, WALL_HEIGHT)); 
      } 
     } 
     return r;   
    } 
} 
private R castRayInY(double angleRay,double direction) { 
// System.out.println("cast ray 2 Y "+angleRay+" "+direction); 
    double angle = angleRay+direction; 
    double x1 = eye.getX()+SQUARE_SIZE*Math.cos(angle); 
    double y1 = eye.getY()+SQUARE_SIZE*Math.sin(angle); 
// System.out.println(eye+" "+x1+" "+y1); 
    double slope = (y1-eye.getY())/(x1-eye.getX()); 
    if (Math.sin(angle)==0) { 
     if (Math.cos(angle)>0) 
      return new R(Double.MAX_VALUE,Math.PI,BACKGROUND,null); 
     else 
      return new R(Double.MAX_VALUE,0,BACKGROUND,null); 
    } 
    if (Math.sin(angle)>0) { 
     int firstY = ((eye.getY()/SQUARE_SIZE)+1)*SQUARE_SIZE; 
     R r = new R(Double.MAX_VALUE,angle+Math.PI,BACKGROUND,null); 
     for (int y = firstY; y<map.length*SQUARE_SIZE; y += SQUARE_SIZE) { 
      int x = (int)((y-eye.getY())/slope)+eye.getX(); 
      if (isOutside(x,y,Color.CYAN,this.showRayCastingY)) break; 
      Color c = colorAt(x,y); 
      if (c==null) c = colorAt(x,y-1); 
      if (c==null) c = colorAt(x-1,y); 
      if (c==null) c = colorAt(x-1,y-1); 
      if (c!=null) { 
       double DX = x-eye.getX(); 
       int DY = y-eye.getY(); 
       return new R(Math.sqrt(DX*DX+DY*DY),3*Math.PI/2,c,new Plot((int)x,(int)y, WALL_HEIGHT)); 
       } 
      } 
      return r; 
     } else { 
      int firstY = ((eye.getY()/SQUARE_SIZE))*SQUARE_SIZE; 
      R r = new R(Double.MAX_VALUE,angle+Math.PI,BACKGROUND,null); 
      for (int y = firstY; y>=0; y -= SQUARE_SIZE) { 
       int x = (int)((y-eye.getY())/slope)+eye.getX(); 
       if (isOutside(x,y,Color.CYAN,this.showRayCastingY)) break; 
       Color c = colorAt(x,y); 
       if (c==null) c = colorAt(x,y-1); 
       if (c==null) c = colorAt(x-1,y); 
       if (c==null) c = colorAt(x-1,y-1); 
       if (c!=null) { 
        double DX = x-eye.getX(); 
        int DY = y-eye.getY(); 
        return new R(Math.sqrt(DX*DX+DY*DY),Math.PI/2,c,new Plot((int)x,(int)y, WALL_HEIGHT)); 
       } 
      } 
      return r;   
     } 
    } 

マイRクラスは今のPlot (x, y, z)を持つ別の高さ寸法とを行うしたいと思います。今のところこれは動作しますが、私はcastRayInZのような新しい関数を追加したいと思いますが、私はすべての数学の理論を持っていません。私の迷路は、そのようなマップから構成されます。

XYZは色(私の光機能をテストするブルーための赤、緑のためのYとZのためのX)のためであると私はそれぞれの正方形のための高さを追加
private String [][]map = { // each: SQUARE_SIZE x SQUARE_SIZE 
     { "Y300", "Z500", "X230", "Y112", "Z321", "X120", "X354" }, 
     { "X89", " ", " ", " ", "Y120", " ", "X232" }, 
     { "Z124", " ", "X276", " ", "X123", " ", "X" }, 
     { "Y290", " ", " ", " ", " ", " ", "X100" }, 
     { "X32", "Z430", " ", "Y500", "X120", " ", "X123" }, 
     { "X222", " ", " ", " ", " ", " ", "X210" }, 
     { "X12", "Y98", "Y763", "X146", "Y111", "Y333", "X321" } 

私の地図。私はすべての長さをSQUARE_LENGTHに設定しました。後で、各四角形のサイズをピクセルに縮小し、それを生成することによってマップを拡大します。しかし、私は本当にどのように私は各正方形の高さを変更することができます知りたい。私は今、4日以来、それに取り組んでいると私は、任意の手掛かりを持っていない...

EDIT私はいくつかのニュースを持っている

は、私は私の壁の大きさを変えたが、私はいくつかの奇妙なものを持っています、ここではスクリーンショットです:

Strange stuff image

あなたは、私はいくつかの奇妙なものがここに表示されますがあります見ることができますのように。ここに私のコードは次のとおりです。

private void castRay(int xOnScreen,double angle,double direction) { 
    R rx = castRayInX(angle,direction); 
    R ry = castRayInY(angle,direction); 
    // In case of out-of-space rays 
    if (rx.getDistance()==Double.MAX_VALUE && ry.getDistance()==Double.MAX_VALUE) { 
     graphics.setColor(BACKGROUND); 
     graphics.drawLine(xOnScreen,0,xOnScreen,this.image.getHeight()); 
     return; 
    } 
    double distance = rx.getDistance(); 
    double normal = rx.getNormal(); 
    Color c = rx.getColor(); 
    double coef = Math.cos((angle+direction+Math.PI)-normal); 
    Plot collision = rx.getPlot(); 

    if (ry.getDistance()<rx.getDistance()) { 
     distance = ry.getDistance(); 
     normal = ry.getNormal(); 
     c = ry.getColor(); 
     coef = Math.cos((angle+direction+Math.PI)-normal); 
     collision = ry.getPlot(); 
    } 

    coef = Math.abs(coef); 
    int factor = map.length*SQUARE_SIZE; 
    double d = (double)(distance+factor)/factor; 
    coef *= 1/(d*d); 
    Color c2 = new Color((int)(c.getRed()*coef),(int)(c.getGreen()*coef),(int)(c.getBlue()*coef)); 
graphics.setColor(c); 
    distance *= Math.cos(angle); // lens correction 
    int h; 
    int hw = (int)(this.screenDistance/distance*WALL_HEIGHT); //WALL_HEIGHT value is 300px at default 
    if(rx.getPlot() != null) 
     h = (int)(this.screenDistance/distance*rx.getPlot().getZ()); // perspective height 
    else 
     h = (int)(this.screenDistance/distance*WALL_HEIGHT); 
    int vh = this.image.getHeight(); 
    int y0 = (hw+vh)/2; 
    int y1 = (vh-h)/2; 
    graphics.drawLine(xOnScreen,y0,xOnScreen,y1); 
    drawEye(direction,collision); 

私の問題はcastRayInX関数から次のようになります。

private R castRayInX(double angleRay,double direction) { 
    double angle = angleRay+direction; 
    double x1 = eye.getX()+SQUARE_SIZE*Math.cos(angle); 
    double y1 = eye.getY()+SQUARE_SIZE*Math.sin(angle); 
    double slope = (y1-eye.getY())/(x1-eye.getX()); 
    if (Math.cos(angle)==0) { 
     if (Math.sin(angle)>0) 
      return new R(Double.MAX_VALUE,3*Math.PI/2,BACKGROUND,null); 
     else 
      return new R(Double.MAX_VALUE,Math.PI/2,BACKGROUND,null); 
    } 
    if (Math.cos(angle)>0) { 
     int firstX = ((eye.getX()/SQUARE_SIZE)+1)*SQUARE_SIZE; 
     R r = new R(Double.MAX_VALUE,angle+Math.PI,BACKGROUND,null); 
     for (int x = firstX; x<map[0].length*SQUARE_SIZE; x += SQUARE_SIZE) { 
      int y = (int)(slope*(x-eye.getX())+eye.getY()); 
      if (isOutside(x,y,Color.MAGENTA,this.showRayCastingX)) break; 
      Color c = colorAt(x,y); 
      int z = heightAt(x,y); 
      if (c==null) c = colorAt(x,y-1); 
      if (c==null) c = colorAt(x-1,y); 
      if (c==null) c = colorAt(x-1,y-1); 
      if (z == 0) z = heightAt(x,y-1); 
      if (z == 0) z = heightAt(x-1,y); 
      if (z == 0) z = heightAt(x-1,y-1); 
      if (c!=null) { 
       int DX = x-eye.getX(); 
       double DY = y-eye.getY(); 
       return new R(Math.sqrt(DX*DX+DY*DY),Math.PI,c,new Plot((int)x,(int)y,(int)z)); 
      } 
     } 
     return r; 
    } else { 
     int firstX = ((eye.getX()/SQUARE_SIZE))*SQUARE_SIZE; 
     R r = new R(Double.MAX_VALUE,angle+Math.PI,BACKGROUND,null); 
     for (int x = firstX; x>=0; x -= SQUARE_SIZE) { 
      int y = (int)(slope*(x-eye.getX())+eye.getY()); 
      if (isOutside(x,y,Color.MAGENTA,this.showRayCastingX)) break; 
      Color c = colorAt(x,y); 
      int z = heightAt(x,y); 
      if (c==null) c = colorAt(x,y-1); 
      if (c==null) c = colorAt(x-1,y); 
      if (c==null) c = colorAt(x-1,y-1); 
      if (z == 0) z = heightAt(x,y-1); 
      if (z == 0) z = heightAt(x-1,y); 
      if (z == 0) z = heightAt(x-1,y-1); 
      if (c!=null) { 
       int DX = x-eye.getX(); 
       double DY = y-eye.getY(); 
       return new R(Math.sqrt(DX*DX+DY*DY),0,c,new Plot((int)x,(int)y,(int)z)); 
      } 
     } 
     return r;   
    } 
} 

私はcastRayInZ機能を作るべきか?または、私はzの値を別の場所に持ってくるべきですか?

+1

ご自身でご質問にお答えしていませんか? 'WALL_HEIHGT'ではなく、パッチごとに異なる高さ値が必要です。何が問題になっていますか? –

+0

定数値を使うのは簡単ですが、私はすべての異なる高さを得る方法がわかりません。私はいくつかの奇妙なことがあります – Jack

答えて

4

だから、あなたは明らかにWolfenstein raycasting技術の基礎を知っています。変数の高さを追加するには、これを実行する必要があります。セルごとに

  1. 追加の高さ情報を

    はそう簡単にあなたのマップテーブルmap[][]であなたのセル情報に別の値を追加します。あなたは(ヒットが検出された後)

  2. 更新走査線が、あなたは、各レイごとに垂直線を描画するコードに

    どこかをレンダリング...奇数である文字列としてのものをコーディングします。そこには、走査線の大きさにのようなもの(Y = 0を仮定すると、画面の上部である)を計算する必要があります

    y0 = center_of_view_y + projected_half_size 
    y1 = center_of_view_y - projected_half_size 
    

    をまたに変更する必要があります。

    projected_half_sizeが一定のために計算されたラインサイズである
    y0 = center_of_view_y + projected_size 
    y1 = y0 - 2*projected_half_size*wall_size 
    

    現在のセルの高さは、wall_size=<0,1>はスケールで、center_of_view_yは、ビュー内の水平線のy座標です。これはあなたの壁を地面に置きます。

  3. 更新レイキャスティングあなたが停止し、第一壁を打ったとき、今

    を。可変壁の高さでは、フルサイズ(wall_size=1)の壁に当たったり、地図を使い果たしたときにのみ停止することができます。これを実装するには2つのオプションがあります。

    1. は、すべてのヒットを覚えているし、最後の描画時の高さからではなく、地面からすぐにしかレンダリング
    2. 逆の順序でレンダリングされます。

    最初のオプションは実装が簡単ですが、より多くのメモリを必要とし、速度が遅くなります。 2番目は高速で、リストやスタックは必要ありません。しかし、それは走査線のレンダリングのためにもう少し数学を必要とします(O(1)が右にコーディングされている場合)

    私は一番上のリンクからデモで少し演奏しました。さて結果は次のようになります。

    variable height

    をあなたは自分の身長上記の光線(あなたがその背後に大きな細胞を見ることができる)のために通過されている地図上でハイライト表示された細胞を見ることができるように。

    移動高度の方向(ジャンプ、階段など)を追加すると、終了条件が異なる必要があります(レンダリングされたスキャンラインがビューの一番上に表示されます)。また、y座標の投影部分も異なり、実際のプレーヤーの高度も含める必要があります。

  4. あなたは、トップ側のレンダリングを追加する必要が上側

    を追加します。天井や床をレンダリングするのと同じです。 IIRCオリジナルWolfensteinはこの機能を持っていませんでしたが、後者は疑似3DゲームのようにDOOMのようになりました。

    ありPerspective Vision on Canvasのような多くの可能なアプローチがありますが、私は(我々はすでに十分な情報を得たとして)を実装するのが最も簡単にはテクスチャに垂直走査線座標の上辺部分を計算することであるとちょうどピクセルをコピーだと思います。私たちがすでに知っているように、光線がセルに当たった場所とプレーヤー/カメラの角度もわかっています。詳細については、PCGPE 1.0 Doom techniques

    です。最初のステップでは、裏面のヒットも追加します。それは次のようになります。最初の最後のヒットセルのヒットをチェックすることによって行われ

    back faces

    。今度は最後のyの座標を前のヒット(同じ走査線の)から覚えていれば、最後にyの面の上端の色を実際のyにレンダリングする代わりに背面がヒットした場合(または床/ 。ここでは、このために使用され、緑の色:

    top side

それはここで助けた場合(ベースGDI/VCL)C++の鉱山で、このためのコード:

//--------------------------------------------------------------------------- 
//--- Doom 3D engine ver: 1.000 -------------------------------------- 
//--------------------------------------------------------------------------- 
#ifndef _Doom3D_h 
#define _Doom3D_h 
//--------------------------------------------------------------------------- 
#include <math.h> 
#include <jpeg.hpp> 
#include "performance.h" 
#include "OpenGLrep4d_double.h" 
//--------------------------------------------------------------------------- 
const DWORD _Doom3D_cell_size=10; // 2D map cell size 
const DWORD _Doom3D_wall_size=100; // full height of wall in map 
#define _Doom3D_filter_txr 
//--------------------------------------------------------------------------- 
class Doom3D 
    { 
public: 
    DWORD mxs,mys,**pmap;   // 2D map // txr + height<<16 
    DWORD sxs,sys,**pscr;   // pseudo 3D screen 
    Graphics::TBitmap *scr; 
    DWORD txs,tys,**ptxr,tn;  // 2D textures 
    Graphics::TBitmap *txr,*txr2; // textures, texture mipmaps resolution: /2 and /4 
    double plrx,plry,plrz,plra;  // player position [x,y,z,angle] 
    double view_ang;    // [rad] view angle 
    double focus;     // [cells] view focal length 
    struct _ray 
     { 
     double x,y,l;    // hit or end of map position 
     DWORD hit;     // map cell of hit or 0xFFFFFFFF 
     char typ;     // H/V 
     _ray() {}; 
     _ray(_ray& a) { *this=a; } 
     ~_ray() {}; 
     _ray* operator = (const _ray *a) { *this=*a; return this; } 
     //_ray* operator = (const _ray &a) { ..copy... return this; } 
     }; 
    _ray *ray;      // ray[sxs] 

    keytab keys; 
    DWORD txr_sel; 
    DWORD cell_h; 

    Doom3D(); 
    Doom3D(Doom3D& a) { *this=a; } 
    ~Doom3D(); 
    Doom3D* operator = (const Doom3D *a) { *this=*a; return this; } 
    //Doom3D* operator = (const Doom3D &a) { ..copy... return this; } 

    void map_resize(DWORD xs,DWORD ys); // change map resolution 
    void map_height(DWORD height);  // set height for whole map to convert maps from Wolfenstein3D demo 
    void map_clear();     // clear whole map 
    void map_save(AnsiString name); 
    void map_load(AnsiString name); 
    void scr_resize(DWORD xs,DWORD ys); 
    void txr_load(AnsiString name); 

    void draw(); 
    void update(double dt); 
    void mouse(double x,double y,TShiftState sh) 
     { 
     x=floor(x/_Doom3D_cell_size); if (x>=mxs) x=mxs-1; if (x<0) x=0; 
     y=floor(y/_Doom3D_cell_size); if (y>=mys) y=mys-1; if (y<0) y=0; 
     DWORD xx=x,yy=y; 
     keys.setm(x,y,sh); 
     if (keys.Shift.Contains(ssLeft)) pmap[yy][xx]=(txr_sel)|(cell_h<<16); 
     if (keys.Shift.Contains(ssRight)) pmap[yy][xx]=0xFFFFFFFF; 
     keys.rfsmouse(); 
     } 
    void wheel(int delta,TShiftState sh) 
     { 
     if (sh.Contains(ssShift)) 
      { 
      if (delta<0) { cell_h-=10; if (cell_h<10) cell_h=10; } 
      if (delta>0) { cell_h+=10; if (cell_h>_Doom3D_wall_size) cell_h=_Doom3D_wall_size; } 
      } 
     else{ 
      if (delta<0) { txr_sel--; if (txr_sel==0xFFFFFFFF) txr_sel=tn-1; } 
      if (delta>0) { txr_sel++; if (txr_sel==  tn) txr_sel= 0; } 
      } 
     } 
    }; 
//--------------------------------------------------------------------------- 
Doom3D::Doom3D() 
    { 
    mxs=0; mys=0;       pmap=NULL; 
    sxs=0; sys=0; scr=new Graphics::TBitmap; pscr=NULL; ray=NULL; 
    txs=0; tys=0; txr=new Graphics::TBitmap; ptxr=NULL; tn=0; 
        txr2=new Graphics::TBitmap; 
    plrx=0.0; plry=0.0; plrz=0.0; plra=0.0; 
    view_ang=60.0*deg; 
    focus=0.25; 
    txr_sel=0; 
    cell_h=_Doom3D_wall_size; 

    txr_load("textures128x128.jpg"); 
    map_resize(16,16); 
    map_load("Doom3D_map.dat"); 
    } 
//--------------------------------------------------------------------------- 
Doom3D::~Doom3D() 
    { 
    DWORD y; 
    map_save("Doom3D_map.dat"); 
    if (pmap) { for (y=0;y<mys;y++) delete[] pmap[y]; delete[] pmap; pmap=NULL; } if (ray) delete[] ray; ray=NULL; 
    if (pscr) {          delete[] pscr; pscr=NULL; } if (scr) delete scr; scr=NULL; 
    if (ptxr) {          delete[] ptxr; ptxr=NULL; } if (txr) delete txr; txr=NULL; 
                        if (txr2) delete txr2; txr2=NULL; 
    } 
//--------------------------------------------------------------------------- 
void Doom3D::map_resize(DWORD xs,DWORD ys) 
    { 
    DWORD y; 
    if (pmap) { for (y=0;y<mys;y++) delete[] pmap[y]; delete[] pmap; pmap=NULL; } 
    mys=ys; mxs=xs; pmap=new DWORD*[mys]; for (y=0;y<mys;y++) pmap[y]=new DWORD[mxs]; 
    map_clear(); 
    plrx=(mxs-1)*0.5; plry=(mys-1)*0.5; plrz=0.0; plra=0.0*deg; 
    } 
//--------------------------------------------------------------------------- 
void Doom3D::map_height(DWORD h) 
    { 
    DWORD x,y,c; 
    for (y=0;y<mys;y++) 
    for (x=0;x<mxs;x++) 
     { 
     c=pmap[y][x]; 
     c&=0xFFFF; 
     c|=h<<16; 
     pmap[y][x]=c; 
     } 
    } 
//--------------------------------------------------------------------------- 
void Doom3D::map_clear() 
    { 
    DWORD x,y,c; 
    for (y=0;y<mys;y++) 
    for (x=0;x<mxs;x++) 
     { 
     c=0xFFFFFFFF; 
     if ((x==0)||(x==mxs-1)) c=0; 
     if ((y==0)||(y==mys-1)) c=0; 
     pmap[y][x]=c; 
     } 
    } 
//--------------------------------------------------------------------------- 
void Doom3D::map_save(AnsiString name) 
    { 
    int hnd=FileCreate(name); if (hnd<0) return; 
    DWORD y; 
    y=' PAM'; 
    FileWrite(hnd,&y ,4); // id 
    FileWrite(hnd,&mxs,4); // x resolution 
    FileWrite(hnd,&mys,4); // y resolution 
    for (y=0;y<mys;y++)  // map 
    FileWrite(hnd,pmap[y],mxs<<2); 
    y=' RLP'; 
    FileWrite(hnd,&y ,4); // id 
    FileWrite(hnd,&plrx,8); 
    FileWrite(hnd,&plry,8); 
    FileWrite(hnd,&plrz,8); 
    FileWrite(hnd,&plra,8); 
    FileClose(hnd); 
    } 
//--------------------------------------------------------------------------- 
void Doom3D::map_load(AnsiString name) 
    { 
    int hnd=FileOpen(name,fmOpenRead); if (hnd<0) return; 
    DWORD x,y; 
    y=' PAM'; FileRead(hnd,&x ,4); // id 
    if (x==y) 
     { 
     FileRead(hnd,&x,4); // x resolution 
     FileRead(hnd,&y,4); // y resolution 
     map_resize(x,y); 
     for (y=0;y<mys;y++) // map 
     FileRead(hnd,pmap[y],mxs<<2); 
     } 
    y=' RLP'; FileRead(hnd,&x ,4); // id 
    if (x==y) 
     { 
     FileRead(hnd,&plrx,8); 
     FileRead(hnd,&plry,8); 
     FileRead(hnd,&plrz,8); 
     FileRead(hnd,&plra,8); 
     } 
    FileClose(hnd); 
    } 
//--------------------------------------------------------------------------- 
void Doom3D::scr_resize(DWORD xs,DWORD ys) 
    { 
    scr->HandleType=bmDIB; 
    scr->PixelFormat=pf32bit; 
    scr->SetSize(xs,ys); 
    sxs=scr->Width; 
    sys=scr->Height; 
    delete[] pscr; pscr=new DWORD*[sys]; 
    for (DWORD y=0;y<sys;y++) pscr[y]=(DWORD*)scr->ScanLine[y]; 
    if (ray) delete[] ray; ray=new _ray[sxs]; 
    } 
//--------------------------------------------------------------------------- 
void Doom3D::txr_load(AnsiString name) 
    { 
    AnsiString ext=ExtractFileExt(name).LowerCase(); 
    for(;;) 
     { 
     if (ext==".bmp") 
      { 
      txr->LoadFromFile(name); 
      break; 
      } 
     if (ext==".jpg") 
      { 
      TJPEGImage *jpg=new TJPEGImage; 
      if (jpg==NULL) return; 
      jpg->LoadFromFile(name); 
      txr->Assign(jpg); 
      delete jpg; 
      break; 
      } 
     return; 
     } 
    DWORD y=tys; 
    txr->HandleType=bmDIB; 
    txr->PixelFormat=pf32bit; 
    txs=txr->Width; 
    tys=txr->Height; 
    // mip map 
    txr2->SetSize(txs>>1,(tys>>1)+(tys>>2)); 
    txr2->Canvas->StretchDraw(TRect(0,  0,txs>>1,tys>>1),txr); 
    txr2->Canvas->StretchDraw(TRect(0,tys>>1,txs>>2,(tys>>1)+(tys>>2)),txr); 
    tn=txs/tys; txs=tys; 
    delete[] ptxr; ptxr=new DWORD*[tys]; 
    for (y=0;y<tys;y++) ptxr[y]=(DWORD*)txr->ScanLine[y]; 
    } 
//--------------------------------------------------------------------------- 
void Doom3D::draw() 
    { 
    // total time measurement 
    tbeg(); double tperf0=performance_tms; 

    AnsiString tcls,tray,tmap,ttotal; 
    double a,a0,da,dx,dy,l,mx,my; 
    DWORD x,y,xs2,ys2,c,m; 
    double xx0,yy0,dx0,dy0,ll0; DWORD c0,d0; 
    double xx1,yy1,dx1,dy1,ll1; DWORD c1,d1; 
    _ray *p; 
    xs2=sxs>>1; 
    ys2=sys>>1; 

    // aspect ratio,view angle corrections 
    a=90.0*deg-view_ang; 
    double wall=double(sxs)*(1.25+(0.288*a)+(2.04*a*a)); // [px] 

    // floor,ceilling/sky 
    tbeg(); 
    for (y=0;y<ys2;y++) for (x=0;x<sxs;x++) pscr[y][x]=0x000080FF; 
    for ( ;y<sys;y++) for (x=0;x<sxs;x++) pscr[y][x]=0x00404040; 
    tend(); tcls=tstr(1)+" cls"; 

    // [cast rays] 
    tbeg(); 
    // diffuse + ambient lighting 
    DWORD ch=155.0+fabs(100.0*sin(plra)); 
    DWORD cv=155.0+fabs(100.0*cos(plra)); 
    a0=plra-(0.5*view_ang); 
    da=divide(view_ang,sxs-1); 
    mx=mxs; my=mys; 
    for (p=ray,a=a0,x=0;x<sxs;x++,a+=da,p++) 
     { 
     p->x=plrx; 
     p->y=plry; 
     p->hit=0xFFFFFFFF; 
     p->typ=' '; 
     p->l=1.0e20; 
     ll0=ll1=p->l; 
     // grid V-line hits 
     c0=0; dx0=cos(a); 
     if (dx0<0.0) { c0=1; xx0=floor(plrx)-0.001; dx0=-1.0; } 
     if (dx0>0.0) { c0=1; xx0=ceil (plrx)+0.001; dx0=+1.0; } 
     if (c0) { dy0=tan(a); yy0=plry+((xx0-plrx)*dy0);    dy0*=dx0; dx=xx0-plrx; dy=yy0-plry; ll0=(dx*dx)+(dy*dy); } 
     // grid H-line hits 
     c1=0; dy1=sin(a); 
     if (dy1<0.0) { c1=1; yy1=floor(plry)-0.001; dy1=-1.0; } 
     if (dy1>0.0) { c1=1; yy1=ceil (plry)+0.001; dy1=+1.0; } 
     if (c1) { dx1=divide(1.0,tan(a)); xx1=plrx+((yy1-plry)*dx1); dx1*=dy1; dx=xx1-plrx; dy=yy1-plry; ll1=(dx*dx)+(dy*dy); } 
     int height0=sys; // already rendered height [pixels] 
     bool _hit,_back=false,_bck=true; 
     if (!c0) ll0=1e20; 
     if (!c1) ll1=1e20; 
     for (;c0||c1;) 
      { 
      _hit=false; 
      // grid V-line hits 
      if (c0) 
       { 
       if (xx0<0.0) { c0=0; ll0=1e20; } 
       if (xx0>=mx) { c0=0; ll0=1e20; } 
       if (yy0<0.0) { c0=0; ll0=1e20; } 
       if (yy0>=my) { c0=0; ll0=1e20; } 
       } 
      if ((c0)&&(ll0<ll1)) 
       { 
       m=DWORD(xx0-dx0); 
       if ((m>=0.0)&&(m<mxs)&&(!_bck)){ c=pmap[DWORD(yy0)][  m ]; if ((c&0xFFFF)!=0xFFFF) { p->hit=c; p->typ='V'; p->l=ll0; p->x=xx0; p->y=yy0; _hit=true; _back=true; _bck=true; }} 
       if (!_hit)      { c=pmap[DWORD(yy0)][DWORD(xx0)]; if ((c&0xFFFF)!=0xFFFF) { p->hit=c; p->typ='V'; p->l=ll0; p->x=xx0; p->y=yy0; _hit=true; _back=false; _bck=false; } xx0+=dx0; dx=xx0-plrx; yy0+=dy0; dy=yy0-plry; ll0=(dx*dx)+(dy*dy); } 
       } 
      // grid H-line hits 
      if (c1) 
       { 
       if (xx1<0.0) { c1=0; ll1=1e20; } 
       if (xx1>=mx) { c1=0; ll1=1e20; } 
       if (yy1<0.0) { c1=0; ll1=1e20; } 
       if (yy1>=my) { c1=0; ll1=1e20; } 
       } 
      if ((c1)&&(ll0>ll1)&&(!_hit)) 
       { 
       m=DWORD(yy1-dy1); 
       if ((m>=0.0)&&(m<mys)&&(!_bck)){ c=pmap[  m ][DWORD(xx1)]; if ((c&0xFFFF)!=0xFFFF) { p->hit=c; p->typ='H'; p->l=ll1; p->x=xx1; p->y=yy1; _hit=true; _back=true; _bck=true; }} 
       if (!_hit)      { c=pmap[DWORD(yy1)][DWORD(xx1)]; if ((c&0xFFFF)!=0xFFFF) { p->hit=c; p->typ='H'; p->l=ll1; p->x=xx1; p->y=yy1; _hit=true; _back=false; _bck=false; } xx1+=dx1; dx=xx1-plrx; yy1+=dy1; dy=yy1-plry; ll1=(dx*dx)+(dy*dy); } 
       } 
      // render scan line 
      if (_hit) 
       { 
       union { DWORD dd; BYTE db[4]; } cc; 
       int tx,ty,sy,sy0,sy1,cnt,dsy,dty; 
       p->l=sqrt(p->l)*cos(a-plra);// anti fish eye 
       m=divide(wall*focus,p->l); // projected wall half size 
       c=0; 
       if (p->typ=='H') { c=ch; tx=double(double(txs)*(p->x-floor(p->x))); } 
       if (p->typ=='V') { c=cv; tx=double(double(txs)*(p->y-floor(p->y))); } 
       tx+=txs*(p->hit&0xFFFF); 

       // prepare interpolation 
       sy1=ys2+m; 
//    sy0=ys2-m;           // constant wall height 
       sy0=sy1-(((m+m)*(p->hit>>16))/_Doom3D_wall_size); // variable wall height 
       dty=tys-1; 
       dsy=sy1-sy0+1; 
       // skip sy>=sys 
       if (sy1>=sys) sy1=sys-1; 
       // skip sy<0 
       for (cnt=dsy,sy=sy0,ty=0;sy<0;sy++) { cnt-=dty; while (cnt<=0) { cnt+=dsy; ty++; }} 

       #ifdef _Doom3D_filter_txr 
       DWORD r=0,g=0,b=0,n=0; 
       #else 
       cc.dd=ptxr[ty][tx]; 
       cc.db[0]=DWORD((DWORD(cc.db[0])*c)>>8); 
       cc.db[1]=DWORD((DWORD(cc.db[1])*c)>>8); 
       cc.db[2]=DWORD((DWORD(cc.db[2])*c)>>8); 
       #endif 
       // continue sy>=0 
       y=height0; 
       if (sy1>height0) sy1=height0; 
       if (sy0<height0) height0=sy0; 
       if (_back){ for (sy=sy0;sy<=y;sy++){ if ((sy>0)&&(sy<sys)) pscr[sy][x]=0x0000FF00; }} 
       else for (;sy<=sy1;sy++) 
        { 
        #ifdef _Doom3D_filter_txr 
        if (!n) 
         { 
         cc.dd=ptxr[ty][tx]; 
         b+=DWORD(cc.db[0]); 
         g+=DWORD(cc.db[1]); 
         r+=DWORD(cc.db[2]); n+=256; 
         } 
        if ((sy>0)&&(sy<sys)) 
         { 
         cc.db[0]=DWORD(c*b/n); b=0; 
         cc.db[1]=DWORD(c*g/n); g=0; 
         cc.db[2]=DWORD(c*r/n); r=0; n=0; 
         pscr[sy][x]=cc.dd; 
         } 
        cnt-=dty; while (cnt<=0) 
         { 
         cnt+=dsy; ty++; 
         cc.dd=ptxr[ty][tx]; 
         b+=DWORD(cc.db[0]); 
         g+=DWORD(cc.db[1]); 
         r+=DWORD(cc.db[2]); n+=256; 
         } 
        #else 
        if ((sy>0)&&(sy<sys)) pscr[sy][x]=cc.dd; 
        cnt-=dty; while (cnt<=0) 
         { 
         cnt+=dsy; ty++; 
         cc.dd=ptxr[ty][tx]; 
         cc.db[0]=DWORD((DWORD(cc.db[0])*c)>>8); 
         cc.db[1]=DWORD((DWORD(cc.db[1])*c)>>8); 
         cc.db[2]=DWORD((DWORD(cc.db[2])*c)>>8); 
         } 
        #endif 
        } 
       if (height0<0) break; 
       } 
      } 
     } 
    tend(); tray=tstr(1)+" ray"; 

    // [2D map] 
    tbeg(); 
    m=_Doom3D_cell_size; 
    mx=_Doom3D_cell_size; 
    if ((sxs>=mxs*m)&&(sys>=mys*m)) 
     { 
     for (y=0;y<mys*m;y++)  // pmap[][] 
     for (x=0;x<mxs*m;x++) 
      { 
      if ((pmap[y/m][x/m]&0xFFFF)!=0xFFFF) c=0x00808080; else c=0x00000000; 
      pscr[y][x]=c; 
      } 
     x=double(plrx*mx);   // view rays 
     y=double(plry*mx); 
     scr->Canvas->Pen->Color=0x00005050; 
     scr->Canvas->Pen->Mode=pmMerge; 
     for (c=0;c<sxs;c++) 
      { 
      scr->Canvas->MoveTo(x,y); 
      scr->Canvas->LineTo(DWORD(ray[c].x*mx),DWORD(ray[c].y*mx)); 
      } 
     scr->Canvas->Pen->Mode=pmCopy; 
     c=focus*m;     // player and view direction 
     scr->Canvas->Pen->Color=0x000000FF; 
     scr->Canvas->Brush->Color=0x000000FF; 
     scr->Canvas->MoveTo(x,y); 
     scr->Canvas->LineTo(DWORD(ray[xs2].x*mx),DWORD(ray[xs2].y*mx)); 
     scr->Canvas->Ellipse(x-c,y-c,x+c,y+c); 
     scr->Canvas->Pen->Color=0x00202020; 
     for (y=0;y<=mys;y++)  // map grid 
     for (x=0;x<=mxs;x++) 
      { 
      scr->Canvas->MoveTo(0 ,y*m); 
      scr->Canvas->LineTo(mxs*m,y*m); 
      scr->Canvas->MoveTo(x*m, 0); 
      scr->Canvas->LineTo(x*m,mys*m); 
      } 
     x=keys.mx*m;    // selected cell 
     y=keys.my*m; 
     scr->Canvas->Pen->Color=0x0020FFFF; 
     scr->Canvas->MoveTo(x ,y ); 
     scr->Canvas->LineTo(x+m,y ); 
     scr->Canvas->LineTo(x+m,y+m); 
     scr->Canvas->LineTo(x ,y+m); 
     scr->Canvas->LineTo(x ,y ); 
     } 
    tend(); tmap=tstr(1)+" map"; 

    // [editor] 
    if (txr_sel!=0xFFFFFFFF) 
     { 
     int x=sxs,y=5,s0,s1,s2,i,j; 
     s0=txs>>1; 
     s1=txs>>2; 
     s2=(s0*cell_h)/_Doom3D_wall_size; 

     for (i=-3;i<=3;i++) 
      { 
      j=txr_sel+i; 
      while (j< 0) j+=tn; 
      while (j>=tn) j-=tn; 
      if (i) { scr->Canvas->CopyRect(TRect(x-s1,y+(s1>>1),x,s1+(s1>>1)),txr2->Canvas,TRect(s1*j,s0,s1*j+s1,s0+s1)); x-=s1+5; } 
      else { scr->Canvas->CopyRect(TRect(x-s0,y+s0-s2 ,x,s0  ),txr2->Canvas,TRect(s0*j, 0,s0*j+s0,s2 )); x-=s0+5; } 
      } 
     } 

    // total time measurement 
    performance_tms=tperf0; 
    tend(); ttotal=tstr(1)+" total"; 

    x=m*mxs+m; 
    c=16; y=-c; 
    scr->Canvas->Font->Color=clYellow; 
    scr->Canvas->Brush->Style=bsClear; 
    scr->Canvas->TextOutA(x,y+=c,AnsiString().sprintf("player: %.2lf x %.2lf x %.2lf",plrx,plry,plrz)); 
    scr->Canvas->TextOutA(x,y+=c,AnsiString().sprintf(" mouse: %.2lf x %.2lf",keys.mx,keys.my)); 
    scr->Canvas->TextOutA(x,y+=c,tray); 
    scr->Canvas->TextOutA(x,y+=c,tcls); 
    scr->Canvas->TextOutA(x,y+=c,tmap); 
    scr->Canvas->TextOutA(x,y+=c,ttotal); 
    scr->Canvas->TextOutA(x,y+=c,AnsiString().sprintf(" key: %d",keys.Key)); 

    // aspect ratio test 
/* 
    c=ys2*7/10; 
    scr->Canvas->Rectangle(xs2-c,ys2-c,xs2+c,ys2+c); 
*/ 
    // cross 
    c=4,m=32; 
    scr->Canvas->Pen->Color=clRed; 
    scr->Canvas->MoveTo(xs2-c,ys2-m); 
    scr->Canvas->LineTo(xs2-c,ys2-c); 
    scr->Canvas->LineTo(xs2-m,ys2-c); 
    scr->Canvas->MoveTo(xs2+c,ys2-m); 
    scr->Canvas->LineTo(xs2+c,ys2-c); 
    scr->Canvas->LineTo(xs2+m,ys2-c); 
    scr->Canvas->MoveTo(xs2-c,ys2+m); 
    scr->Canvas->LineTo(xs2-c,ys2+c); 
    scr->Canvas->LineTo(xs2-m,ys2+c); 
    scr->Canvas->MoveTo(xs2+c,ys2+m); 
    scr->Canvas->LineTo(xs2+c,ys2+c); 
    scr->Canvas->LineTo(xs2+m,ys2+c); 

    scr->Canvas->Brush->Style=bsSolid; 
    } 
//--------------------------------------------------------------------------- 
void Doom3D::update(double dt) 
    { 
    int move=0; 
    double da=120.0*deg*dt; 
    double dl= 5.0 *dt; 
    double dx=0.0,dy=0.0,dz=0.0; 
    if (keys.get(104)) { plra-=da; if (plra< 0.0) plra+=pi2; }      // turn l/r 
    if (keys.get(105)) { plra+=da; if (plra>=pi2) plra-=pi2; } 
    if (keys.get(101)) { move=1; dx=+dl*cos(plra); dy=+dl*sin(plra); }    // move f/b 
    if (keys.get(98)) { move=1; dx=-dl*cos(plra); dy=-dl*sin(plra); } 
    if (keys.get(102)) { move=1; dx= dl*cos(plra-90*deg); dy=dl*sin(plra-90*deg); } // strafe l/r 
    if (keys.get(99)) { move=1; dx= dl*cos(plra+90*deg); dy=dl*sin(plra+90*deg); } 
    if (keys.get(100)) { move=1; dz=+dl; } // strafe u/d 
    if (keys.get(97)) { move=1; dz=-dl; } 
    if (move) // update/test plr position 
     { 
     double x,y,z,mx,my; 
     x=plrx+dx; mx=mxs-focus; 
     y=plry+dy; my=mys-focus; 
     z=plrz+dz; if ((z>=0.0)&&(z<=_Doom3D_wall_size)) plrz=z;; 
     if (x<focus) x=focus; if (x>mx) x=mx; 
     if (y<focus) y=focus; if (y>my) y=my; 
     dx*=divide(focus,dl); 
     dy*=divide(focus,dl); 
      if ((pmap[DWORD(y+dy)][DWORD(x+dx)]&0xFFFF)==0xFFFF) { plrx=x; plry=y; } 
     else if ((pmap[DWORD(y+dy)][DWORD(x )]&0xFFFF)==0xFFFF)   plry=y; 
     else if ((pmap[DWORD(y )][DWORD(x+dx)]&0xFFFF)==0xFFFF) plrx=x; 
     } 
    keys.rfskey(); 
    } 
//--------------------------------------------------------------------------- 
//--------------------------------------------------------------------------- 
#endif 
//--------------------------------------------------------------------------- 
//--------------------------------------------------------------------------- 

だけperformance.h時間測定を無視tbeg,tend,tstrOpenGLrep4d_double.hキーボードとマウスのハンドラkeytabとポートVCL関連のもの(Canvas,AnsiString、ファイルアクセス、JPEG ...)。

あなたが助けが必要な場合は、GFXのものを理解し

を参照してください。このクラスの使用法は簡単な宣言は、このクラスのオブジェクトであり、あなたのウィンドウにイベントを追加します(マウス、キーボード、再ペイント...)。マイVCLウィンドウ(その上に1つのタイマーを備えた単一フォーム)のコードは次のようになります。

//$$---- Form CPP ---- 
//--------------------------------------------------------------------------- 
#include <vcl.h> 
#pragma hdrstop 
#include "win_main.h" 
#include "Doom3D.h" 
//--------------------------------------------------------------------------- 
#pragma package(smart_init) 
#pragma resource "*.dfm" 
TMain *Main; 
Doom3D game; 
//--------------------------------------------------------------------------- 
void TMain::draw() 
    { 
    game.draw(); 
    Canvas->Draw(0,0,game.scr); 
    } 
//--------------------------------------------------------------------------- 
__fastcall TMain::TMain(TComponent* Owner) : TForm(Owner) 
    { 
    } 
//--------------------------------------------------------------------------- 
void __fastcall TMain::FormResize(TObject *Sender) 
    { 
    game.scr_resize(ClientWidth,ClientHeight); 
    } 
//--------------------------------------------------------------------------- 
void __fastcall TMain::tim_redrawTimer(TObject *Sender) 
    { 
    game.update(tim_redraw->Interval*0.001); 
    draw(); 
    } 
//--------------------------------------------------------------------------- 
void __fastcall TMain::FormKeyDown(TObject *Sender, WORD &Key,TShiftState Shift){ game.keys.set(Key,Shift); } 
void __fastcall TMain::FormKeyUp(TObject *Sender, WORD &Key, TShiftState Shift) { game.keys.rst(Key,Shift); } 
void __fastcall TMain::FormActivate(TObject *Sender)       { game.keys.reset_keys(); } 
//--------------------------------------------------------------------------- 
void __fastcall TMain::FormMouseMove(TObject *Sender,      TShiftState Shift, int X, int Y) { game.mouse(X,Y,Shift); } 
void __fastcall TMain::FormMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y) { game.mouse(X,Y,Shift); } 
void __fastcall TMain::FormMouseUp (TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y) { game.mouse(X,Y,Shift); } 
void __fastcall TMain::FormMouseWheel(TObject *Sender, TShiftState Shift, int WheelDelta, TPoint &MousePos, bool &Handled) { game.wheel(WheelDelta,Shift); Handled=true; } 
//--------------------------------------------------------------------------- 

そして、ここでの主な反復変数の説明:

variables

そしてここでテクスチャファイル:

textures

ここでコードの調整を行った後の様子を示します。

jump

+0

x、y座標の壁の高さを返す 'heightAt(double x、double y)'関数を追加すると動作しますか?私はそれをして、私は 'castRayInX'関数で呼びましたが、時にはnullのPlotオブジェクトを返しますが、壁があるので奇妙なものが表示されます。 – Jack

+0

@Jack奇妙なことはどういう意味ですか?(画像がありますか?)私は私の答えにいくつかの情報とそれがどのように見えるべきかのイメージを追加しました(私はwolfenstein rayキャストエンジンに最初の3ステップを実装しました)。最後の部分は十分な時間と気分がないので待たなければなりません。 – Spektre

+0

私はスクリーンショットを作成しました。私の 'WALL_HEIGHT'値は' 300px'です。 – Jack

関連する問題