Pygame'de SNES Mode 7 (afin dönüşüm) etkisi yapma


19

Pygame'de Mode 7 / mario kart tipi efektinin nasıl yapılacağı hakkında kısa bir cevap var mı?

Çok fazla googledim, gelebileceğim tüm dokümanlar diğer dillerde onlarca sayfa (asm, c) çok garip görünümlü denklemler ve benzeri.

İdeal olarak, İngilizce'de matematiksel terimlerden daha fazla açıklanmış bir şey bulmak istiyorum.

Görüntüyü / dokuyu veya gerekli olan her şeyi manipüle etmek için PIL veya pygame kullanabilirim.

Pygame'de gerçekten bir mod 7 efekti elde etmek istiyorum, ancak zekamın sonuna yaklaştım. Yardım çok takdir edilecektir. Verebildiğim kadar basit olmasa bile, sağlayabileceğiniz tüm kaynaklar veya açıklamalar harika olurdu.

Eğer çözebilirsem, yeni başlayanlar sayfası için mod 7'yi nasıl yapacağımı kesin olarak yazacağım.

düzenleme: mod 7 doc: http://www.coranac.com/tonc/text/mode7.htm


5
burada denklemler var gibi görünüyor: en.wikipedia.org/wiki/Mode_7 Bu günlerde 3D ivmelenmemize rağmen, Mod 7 gibi şeyler ya da kıyameti işe yarayan kıyamet bir çözümden çok bir meraktır.
salmonmoose

3
@ 2D_Bu sayfayı al algoritmayı benim için çok iyi açıklıyor. Bunu nasıl yapacağınızı bilmek mi istiyorsunuz yoksa zaten sizin için mi uygulanmasını istiyorsunuz?
Gustavo Maciel

1
@ stephelton SNES sistemlerinde, bozulabilen, döndürülebilen tek katman .. (matrislerle afin dönüşümler uygulanmıştır) yedinci katmandır. Arka Plan katmanı. Diğer tüm katmanlar basit spritelara alışıktı, Yani bir 3D efekti istiyorsanız, bu katmanı kullanmak zorundaydınız, burası adın geldiği yer :)
Gustavo Maciel

3
@GustavoMaciel: Bu biraz yanlış. SNES'in 8 farklı modu (0-7) vardı, burada 4'e kadar arka plan katmanı farklı işlevselliklere sahipti, ancak sadece bir mod (mod 7, dolayısıyla ad) dönüşü ve ölçeklendirmeyi destekledi (ve ayrıca sizi tek bir katmanla sınırladı). Modları gerçekten birleştiremezsiniz.
Michael Madsen

1
@Michael: Ayrıca şunu da ekleyeceğim: SNES, 90'larda (F-Zero oyunu ile) bu efekti kullanan ilk popüler konsoldan biriydi ve bu yüzden bundan sonra insanlar diğer tüm 2D yatay doku eşlemeli uçak efektlerinden bahsetmeye başladılar. oyunlar "mod 7" olarak. Gerçekte, bu tür bir etki yeni değildi ve uzun zaman önce çarşıda vardı, krş. Uzay Harrier / Hang-On (1985).
tigrou

Yanıtlar:


45

Mod 7 çok basit bir etkidir. Bazı zemine / tavana 2B x / y doku (veya fayans) yansıtır. Eski SNES bunu yapmak için donanım kullanır, ancak modern bilgisayarlar o kadar güçlüdür ki bu gerçek zamanlı olarak yapabilirsiniz (ve belirttiğiniz gibi ASM'ye gerek yoktur).

Bir 3B noktasına (x, y, z) 2B noktasına (x, y) yansıtmak için temel 3B matematik formülü:

x' = x / z;
y' = y / z; 

Bunu düşündüğünüzde, mantıklı. Uzak mesafedeki nesneler yakınınızdaki nesnelerden daha küçüktür. Hiçbir yere varamayacak demiryolu yollarını düşünün:

resim açıklamasını buraya girin

Formül giriş değerlerine tekrar bakarsak: xve işlediğimiz ygeçerli piksel zolacak ve noktanın ne kadar uzak olduğuna dair mesafe bilgisi olacaktır. Ne zolması gerektiğini anlamak için, bu resme bakın, zyukarıdaki görüntü için değerleri gösterir :

resim açıklamasını buraya girin

mor = yakın mesafe, kırmızı = uzak

Yani bu örnekte, zdeğer y - horizon( (x:0, y:0)ekranın ortasında olduğu varsayılarak )

Her şeyi bir araya getirirsek, olur: (sözde kod)

for (y = -yres/2 ; y < yres/2 ; y++)
  for (x = -xres/2 ; x < xres/2 ; x++)
  {
     horizon = 20; //adjust if needed
     fov = 200; 

     px = x;
     py = fov; 
     pz = y + horizon;      

     //projection 
     sx = px / pz;
     sy = py / pz; 

     scaling = 100; //adjust if needed, depends of texture size
     color = get2DTexture(sx * scaling, sy * scaling);  

     //put (color) at (x, y) on screen
     ...
  }

Son bir şey: Eğer bir mario kart oyunu yapmak istiyorsanız, sanırım haritayı da döndürmek istiyorsunuz. Aynı zamanda çok kolay: döndürün sxve sydoku değeri almadan önce. İşte formül:

  x' = x * cos(angle) - y * sin(angle);
  y' = x * sin(angle) + y * cos(angle);

ve harita üzerinde hareket etmek istiyorsanız, doku değeri almadan önce bir miktar ofset ekleyin:

  get2DTexture(sx * scaling + xOffset, sy * scaling + yOffset);

NOT: i (neredeyse kopyala-yapıştır) algoritması test ve çalışır. Örnek: http://glslsandbox.com/e#26532.3 (son tarayıcı ve WebGL'nin etkin olmasını gerektirir)

resim açıklamasını buraya girin

NOT2: Basit bir şey istediğinizi söylediğiniz için basit matematik kullanıyorum (ve vektör matematiğine aşina görünmüyor). Aynı şeyleri wikipedia formülü ya da verdiğiniz öğreticileri kullanarak da elde edebilirsiniz. Yaptıkları yol çok daha karmaşıktır, ancak efekti yapılandırmak için çok daha fazla seçeneğiniz vardır (sonunda aynı çalışır ...).

Daha fazla bilgi için şunu okumanızı öneririm: http://en.wikipedia.org/wiki/3D_projection#Perspective_projection


Eklenecek bir şey, açının günah ve cos çoğunlukla çerçeve başına sabit olduğundan, tüm x, y konumlarını bulmak için bunları döngü dışında hesapladığınızdan emin olun.
hobberwickey

1

İşte bunu yapmak için kod. Blogumda yaptığım öğreticinin koduyla aynı . Mode 7 yöntemini ve RayCasting'i öğrenmek için buraya bakın.

Temel olarak, sözde kod:

//This is the pseudo-code to generate the basic mode7

for each y in the view do
    y' <- y / z
    for each x in the view do
        x' <- x / z
        put x',y' texture pixel value in x,y view pixel
    end for
    z <- z + 1
end for

İşte benim öğretici aşağıdaki JAVA yapılan kod.

package mode7;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;

/**
 * Mode 7 - Basic Implementation
 * This code will map a texture to create a pseudo-3d perspective.
 * This is an infinite render mode. The texture will be repeated without bounds.
 * @author VINICIUS
 */
public class BasicModeSeven {

    //Sizes
    public static final int WIDTH = 800;
    public static final int WIDTH_CENTER = WIDTH/2;
    public static final int HEIGHT = 600;
    public static final int HEIGHT_CENTER = HEIGHT/2;

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) throws IOException {

        //Create Frame
        JFrame frame = new JFrame("Mode 7");
        frame.setSize(WIDTH, HEIGHT);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);

        //Create Buffered Images:
        //image - This is the image that will be printed in the render view
        //texture - This is the image that will be mapped to the render view
        BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
        BufferedImage texture = ImageIO.read(new File("src/mode7/texture.png"));

        //The new coords that will be used to get the pixel on the texture
        double _x, _y;

        //z - the incrementable variable that beggins at -300 and go to 300, because 
        //the depth will be in the center of the HEIGHT
        double z =  HEIGHT_CENTER * -1;

        //Scales just to control de scale of the printed pixel. It is not necessary
        double scaleX = 16.0;
        double scaleY = 16.0; 

        //Mode 7 - loop (Left Top to Down)
        for(int y = 0; y < HEIGHT; y++){

            _y = y / z; //The new _y coord generated
            if(_y < 0)_y *= -1; //Control the _y because the z starting with a negative number
            _y *= scaleY; //Increase the size using scale
            _y %= texture.getHeight(); //Repeat the pixel avoiding get texture out of bounds 

            for(int x = 0; x < WIDTH; x++){

                _x = (WIDTH_CENTER - x) / z; //The new _x coord generated
                if(_x < 0)_x *= -1; //Control the _x to dont be negative
                _x *= scaleX; //Increase the size using scale
                _x %= texture.getWidth(); //Repeat the pixel avoiding get texture out of bounds 

                //Set x,y of the view image with the _x,_y pixel in the texture
                image.setRGB(x, y, texture.getRGB((int)_x, (int)_y));
            }

            //Increment depth
            z++;
        }

        //Loop to render the generated image
        while(true){
            frame.getGraphics().drawImage(image, 0, 0, null);
        }
    }
}

Sonuç:

resim açıklamasını buraya girin


Açıklama burada programandocoisas.blogspot.com.br . Orada bu efekti yapmak için adım adım öğretici bulabilirsiniz. Ancak yorumları daha iyi koymak için yazımı güncelleyeceğim;).
Vinícius Biavatti
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.