1

私は自分の目的のために C# で小さな 2D ゲーム エンジンを作成していますが、スプライトの衝突検出を除いては正常に動作します。ピクセルごとの検出にすることにしました (実装が最も簡単です) が、想定どおりに機能していません。コードは、衝突が発生するずっと前に衝突を検出します。検出のすべてのコンポーネントを調べましたが、問題が見つかりません。

衝突検出方法:

public static bool CheckForCollision(Sprite s1, Sprite s2, bool perpixel) {
    if(!perpixel) {
        return s1.CollisionBox.IntersectsWith(s2.CollisionBox);
    }
    else {
        Rectangle rect;
        Image img1 = GraphicsHandler.ResizeImage(GraphicsHandler.RotateImagePoint(s1.Image, s1.Position, s1.Origin, s1.Rotation, out rect), s1.Scale);
        int posx1 = rect.X;
        int posy1 = rect.Y;

        Image img2 = GraphicsHandler.ResizeImage(GraphicsHandler.RotateImagePoint(s2.Image, s2.Position, s2.Origin, s2.Rotation, out rect), s2.Scale);
        int posx2 = rect.X;
        int posy2 = rect.Y;

        Rectangle abounds = new Rectangle(posx1, posy1, (int)img1.Width, (int)img1.Height);
        Rectangle bbounds = new Rectangle(posx2, posy2, (int)img2.Width, (int)img2.Height);

        if(Utilities.RectangleIntersects(abounds, bbounds)) {

            uint[] bitsA = s1.GetPixelData(false);

            uint[] bitsB = s2.GetPixelData(false);

            int x1 = Math.Max(abounds.X, bbounds.X);
            int x2 = Math.Min(abounds.X + abounds.Width, bbounds.X + bbounds.Width);

            int y1 = Math.Max(abounds.Y, bbounds.Y);
            int y2 = Math.Min(abounds.Y + abounds.Height, bbounds.Y + bbounds.Height);

            for(int y = y1; y < y2; ++y) {
                for(int x = x1; x < x2; ++x) {
                    if(((bitsA[(x - abounds.X) + (y - abounds.Y) * abounds.Width] & 0xFF000000) >> 24) > 20 &&
                        ((bitsB[(x - bbounds.X) + (y - bbounds.Y) * bbounds.Width] & 0xFF000000) >> 24) > 20)
                        return true;
                }
            }
        }
        return false;
    }
}

画像の回転方法:

internal static Image RotateImagePoint(Image img, Vector pos, Vector orig, double rotation, out Rectangle sz) {
    if(!(new Rectangle(new Point(0), img.Size).Contains(new Point((int)orig.X, (int)orig.Y))))
        Console.WriteLine("Origin point is not present in image bound; unwanted cropping might occur");
    rotation = (double)ra_de((double)rotation);
    sz = GetRotateDimensions((int)pos.X, (int)pos.Y, img.Width, img.Height, rotation, false);
    Bitmap bmp = new Bitmap(sz.Width, sz.Height);
    Graphics g = Graphics.FromImage(bmp);
    g.SmoothingMode = SmoothingMode.AntiAlias;
    g.InterpolationMode = InterpolationMode.HighQualityBicubic;
    g.PixelOffsetMode = PixelOffsetMode.HighQuality;
    g.RotateTransform((float)rotation);
    g.TranslateTransform(sz.Width / 2, sz.Height / 2, MatrixOrder.Append);
    g.DrawImage(img, (float)-orig.X, (float)-orig.Y);
    g.Dispose();
    return bmp;
}       
internal static Rectangle GetRotateDimensions(int imgx, int imgy, int imgwidth, int imgheight, double rotation, bool Crop) {
    Rectangle sz = new Rectangle();
    if (Crop == true) {
        // absolute trig values goes for all angles
        double dera = de_ra(rotation);
        double sin = Math.Abs(Math.Sin(dera));
        double cos = Math.Abs(Math.Cos(dera));
        // general trig rules:
        // length(adjacent) = cos(theta) * length(hypotenuse)
        // length(opposite) = sin(theta) * length(hypotenuse)
        // applied width = lo(img height) + la(img width)
        sz.Width = (int)(sin * imgheight + cos * imgwidth);
        // applied height = lo(img width) + la(img height)
        sz.Height = (int)(sin * imgwidth + cos * imgheight);
    }
    else {
        // get image diagonal to fit any rotation (w & h =diagonal)
        sz.X = imgx - (int)Math.Sqrt(Math.Pow(imgwidth, 2.0) + Math.Pow(imgheight, 2.0));
        sz.Y = imgy - (int)Math.Sqrt(Math.Pow(imgwidth, 2.0) + Math.Pow(imgheight, 2.0));
        sz.Width = (int)Math.Sqrt(Math.Pow(imgwidth, 2.0) + Math.Pow(imgheight, 2.0)) * 2;
        sz.Height = sz.Width;

    }
    return sz;
}

ピクセル取得方法:

public uint[] GetPixelData(bool useBaseImage) {
    Rectangle rect;
    Image image;
    if (useBaseImage)
        image = Image;
    else
        image = GraphicsHandler.ResizeImage(GraphicsHandler.RotateImagePoint(Image, Position, Origin, Rotation, out rect), Scale);

    BitmapData data;
    try {
        data = ((Bitmap)image).LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
    }
    catch (ArgumentException) {
        data = ((Bitmap)image).LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadOnly, image.PixelFormat);
    }

    byte[] rawdata = new byte[data.Stride * image.Height];
    Marshal.Copy(data.Scan0, rawdata, 0, data.Stride * image.Height);
    ((Bitmap)image).UnlockBits(data);
    int pixelsize = 4;
    if (data.PixelFormat == PixelFormat.Format24bppRgb)
        pixelsize = 3;
    else if (data.PixelFormat == PixelFormat.Format32bppArgb || data.PixelFormat == PixelFormat.Format32bppRgb)
        pixelsize = 4;

    double intdatasize = Math.Ceiling((double)rawdata.Length / pixelsize);
    uint[] intdata = new uint[(int)intdatasize];

    Buffer.BlockCopy(rawdata, 0, intdata, 0, rawdata.Length);

    return intdata;
} 

ピクセル取得方法は機能し、回転方法も同様に機能するため、コードが間違っている可能性があるのは衝突検出コードだけですが、どこに問題があるのか​​ 本当にわかりません。

4

3 に答える 3

1

私はあなたの問題を見つけたと思います。

internal static Rectangle GetRotateDimensions(int imgx, int imgy, int imgwidth, int imgheight, double rotation, bool Crop) {
    Rectangle sz = new Rectangle(); // <-- Default constructed rect here.
    if (Crop == true) {
            // absolute trig values goes for all angles
            double dera = de_ra(rotation);
            double sin = Math.Abs(Math.Sin(dera));
            double cos = Math.Abs(Math.Cos(dera));
            // general trig rules:
            // length(adjacent) = cos(theta) * length(hypotenuse)
            // length(opposite) = sin(theta) * length(hypotenuse)
            // applied width = lo(img height) + la(img width)
            sz.Width = (int)(sin * imgheight + cos * imgwidth);
            // applied height = lo(img width) + la(img height)
            sz.Height = (int)(sin * imgwidth + cos * imgheight);

            // <-- Never gets the X & Y assigned !!!
    }

Rectangle の X 座標と Y 座標に imgx と imgy を割り当てたことがないため、GetRotateDimensions を呼び出すたびに、同じ位置に Rectangle が生成されます。サイズは異なる場合がありますが、常にデフォルトの X、Y 位置にあります。これは、2 つのスプライトで衝突を検出しようとするたびに、GetRotateDimensions が実際の場所に関係なく境界を同じ位置に配置するため、非常に早い衝突を引き起こします。

その問題を修正すると、別のエラーが発生する場合があります。

Rectangle rect;
Image img1 = GraphicsHandler.ResizeImage(GraphicsHandler.RotateImagePoint(s1.Image, s1.Position, s1.Origin, s1.Rotation, out rect), s1.Scale);
// <-- Should resize rect here.
int posx1 = rect.X;
int posy1 = rect.Y;

RotateImagePoint 関数から境界矩形を取得しますが、その後、画像のサイズを変更します。rect の X と Y は、おそらくイメージのサイズ変更された境界の X と Y と正確に同じではありません。サイズ変更ですべてのポイントが中心に向かって収縮または中心から拡大している間、画像の中心が所定の位置に留まるという意味だと思います。この場合、正しい位置を取得するために、画像と同様に rect のサイズを変更する必要があります。

于 2009-05-02T06:27:41.687 に答える
1

ここにいる多くの人が、コードを精査して正確に何が間違っているのかを調べることはないと思います。しかし、問題を見つける方法についていくつかのヒントを提供できます。

衝突が発生するずっと前に衝突が発生した場合は、バウンディング ボックスのチェックが適切に機能していないことをお勧めします。

衝突時の四角形に関するすべてのデータをダンプするようにコードを変更します。したがって、衝突時の状況を表示するコードを作成できます。数字を見るより簡単かもしれません。

それとは別に、ピクセルごとの衝突検出を実装する方が簡単だとは思いません。回転とスケーリングを許可すると、すぐに正しくすることが難しくなります。代わりに、ポリゴン ベースの衝突検出を行います。

私はあなたのような独自の 2D エンジンを作成しましたが、ポリゴン ベースの衝突検出を使用しましたが、うまくいきました。

于 2009-04-27T11:50:41.390 に答える
0

これが実際の問題であるとLockBitsは思えませんが、ビットデータが画像の幅に揃えられていることを保証するものではありません。

つまり、いくらかのパディングがあるかもしれません。data[x + y * stride]ではなくを使用して画像にアクセスする必要がありますdata[x + y * width]。Stride も の一部ですBitmapData

于 2009-04-27T11:54:21.130 に答える