XNA 2d कैमरा स्क्रॉलिंग - मैट्रिक्स ट्रांसफॉर्मेशन का उपयोग क्यों करें?


18

मैं एक टेस्ट खेल बना रहा हूं जहां मैं चाहता हूं कि स्तर लगातार स्क्रॉल हो। इस आशय को बनाने के लिए मैंने एक कैमरा क्लास की स्थापना की है जो बस एक वेक्टर 2 स्थिति और एक एनम दिशा को संग्रहीत करता है। इसमें 'चाल' के लिए एक सार्वजनिक विधि भी शामिल है जो एक निश्चित दर पर स्थिति को बदल देती है। मैं तब इस स्थिति का उपयोग करता हूं जब ड्राइंग करते समय मेरी सरणी टाइलों के माध्यम से लूपिंग करता है। यह सब ठीक काम करता है।

हालांकि, मुझे बताया गया है कि मुझे कैमरे को स्थानांतरित करने के लिए एक ट्रांसफ़ॉर्म मैट्रिक्स का उपयोग किया जाना चाहिए, और जब मैं स्प्राइट शुरू करूं तो मुझे यह प्रदान करना चाहिए। मैं थोड़ा उलझन में हूँ।) यह कैसे काम करता है? जैसे कि मैं केवल इसे दे रहा हूं जब स्प्रिटबैच शुरू होता है, तो यह कैसे बदलती स्थिति को रखना जानता है? ख।) टाइल्स के माध्यम से लूप होने पर मुझे निश्चित रूप से कैमरे की स्थिति की आवश्यकता क्यों है?

इस क्षण मैं काम करने के लिए नहीं मिल सकता, लेकिन यह कोई आश्चर्य की बात नहीं है क्योंकि मैं पूरी तरह से नहीं समझता कि यह कैसे काम करने के लिए है। वर्तमान में मेरे प्रयास में (कोड का पालन करने के लिए) टाइलें खींची जा रही हैं, जिसका अर्थ है कि कैमरों की स्थिति बदल रही है, लेकिन व्यूपोर्ट की स्थिति अपरिवर्तित बनी हुई है (यानी कैमरा मूल पर)। मैं वास्तव में कुछ सलाह / मार्गदर्शन की सराहना करता हूं कि इसका उपयोग कैसे किया जाना चाहिए?

कैमरा:

 class Camera {

    // The position of the camera.
    public Vector2 Position {
        get { return mCameraPosition; }
        set { mCameraPosition = value; }
    }
    Vector2 mCameraPosition;

    public Vector2 Origin { get; set; }

    public float Zoom { get; set; }

    public float Rotation { get; set; }

    public ScrollDirection Direction { get; set; }

    private Vector2 mScrollSpeed = new Vector2(20, 18);

    public Camera() {
        Position = Vector2.Zero;
        Origin = Vector2.Zero;
        Zoom = 1;
        Rotation = 0;
    }


    public Matrix GetTransform() {
        return Matrix.CreateTranslation(new Vector3(mCameraPosition, 0.0f)) *
               Matrix.CreateRotationZ(Rotation) *
               Matrix.CreateScale(Zoom, Zoom, 1.0f) *
               Matrix.CreateTranslation(new Vector3(Origin, 0.0f));
    }




    public void MoveCamera(Level level) {
        if (Direction == ScrollDirection.Up)
        {
            mCameraPosition.Y = MathHelper.Clamp(mCameraPosition.Y - mScrollSpeed.Y, 0, (level.Height * Tile.Height - level.mViewport.Height));
        }
    }

स्तर:

 public void Update(GameTime gameTime, TouchCollection touchState) {

            Camera.MoveCamera(this);
 }


 public void Draw(SpriteBatch spriteBatch) {
        //spriteBatch.Begin();
        spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, SamplerState.LinearClamp, DepthStencilState.Default, RasterizerState.CullCounterClockwise, null, mCamera.GetTransform());


        DrawTiles(spriteBatch);

        spriteBatch.End();
    }

खेल - बस स्तर के भीतर ड्रा कहते हैं:

  protected override void Draw(GameTime gameTime)  {
        mGraphics.GraphicsDevice.Clear(Color.Black);

        //mSpriteBatch.Begin();

        // Draw the level.
        mLevel.Draw(mSpriteBatch);

        //mSpriteBatch.End();

        base.Draw(gameTime);
    }

================================================== =============================== EDIT:

सबसे पहले, धन्यवाद आप craftworkgames आपकी मदद के लिए इस प्रकार अब तक।

मैंने सुझाव के साथ खेला। जब मैंने सभी टाइलें खींचीं, तो fr को लगभग 15 से 30 तक टंकी दी - शायद इसलिए कि स्तर बहुत बड़े हैं।

इसलिए मैंने जो किया है वह मैट्रिक्स लागू करें और अपडेट में स्थानांतरित करें (जैसा कि सुझाव दिया गया है) लेकिन ड्रा में मैं टाइल्स के माध्यम से लूपिंग के लिए कैमरों की स्थिति का उपयोग करता हूं (यानी बाईं ओर काउंटर शुरू करें और दाएं टाइल पर समाप्त करें)। यह सब अच्छी तरह से काम करता है और मैं इसके साथ खुश हूँ :-)

मेरा नया मुद्दा खिलाड़ी में है। जाहिर है कि मैं अब स्तर के बजाय कैमरे को आगे बढ़ा रहा हूं, खिलाड़ी को कैमर द्वारा पीछे छोड़ दिया जाता है क्योंकि इसकी स्थिति स्थिर रहती है। मैंने इस समस्या के दो समाधानों के बारे में सोचा है, पहला यह है कि खिलाड़ी को ड्रॉ करते समय केवल कैमरों की स्थिति पर विचार करें। ड्रॉ फंकी में Ie बस खिलाड़ी की स्थिति के लिए कैमरा स्थिति जोड़ें। दूसरा खिलाड़ी के लिए एक नया स्प्राइट बैच शुरू करना है जिसमें कोई परिवर्तन नहीं है। यानी टाइल खींचने के बाद स्प्राइटबैच को खत्म करें, फिर खिलाड़ी को खींचते समय एक नई शुरुआत करें। मुझे पता है कि दोनों काम करेंगे, लेकिन मैं उन पूंछों के प्रमुख नहीं बना सकता जिनमें से प्रदर्शन / अच्छा कोडिंग के मामले में बेहतर होगा? मुझे यकीन नहीं है कि दो बार बैच शुरू करने से प्रदर्शन के निहितार्थ क्या हैं?


1
"मेरा नया मुद्दा खिलाड़ी में है। जाहिर है जैसे मैं अब स्तर के बजाय कैमरे को आगे बढ़ा रहा हूं, खिलाड़ी को कैमर द्वारा पीछे छोड़ दिया जाता है क्योंकि इसकी स्थिति स्थिर रहती है।" बस इसे पढ़ें और मैं इतना उलझन में हूं कि धरती पर आप खिलाड़ी को स्तरीय समन्वय प्रणाली में स्थानांतरित क्यों नहीं करेंगे?
क्लासिकथुंदर

जवाबों:


15

कैमरा मैट्रिक्स रूपांतरण आसान हैं

एक बुनियादी कैमरा बनाना आसान है। नीचे आपको मूल बातों के साथ शुरुआत मिलेगी। इसे घुमाते हुए, घुमाते हुए, चींटियों को चीरते हुए। हर 2d स्प्राइट को हिलाने से कोई समस्या नहीं होती है, लेकिन यदि आप स्केलिंग या रोटेशन में कारक हैं तो व्यक्तिगत रूप से हर स्प्राइट को लागू करना बहुत मुश्किल है।

class Camera2D 
{
    public float Zoom { get; set; }
    public Vector2 Location { get; set; }
    public float Rotation { get; set;}

    private Rectangle Bounds { get; set; }

    private Matrix TransformMatrix
    { 
        get: {
            return 
                Matrix.CreateTranslation(new Vector3(-Location.X, -Location.Y, 0)) *
                Matrix.CreateRotationZ(Rotation) *
                Matrix.CreateScale(Zoom) *
                Matrix.CreateTranslation(new Vector3(Bounds.Width * 0.5f, Bounds.Height * 0.5f, 0));
        }
    };

    public Camera2D(Viewport viewport) 
    {
        Bounds = viewport.Bounds;
    }
}

यह समन्वय प्रणाली परिभाषाओं के बीच रूपांतरण करना वास्तव में आसान बनाता है

स्क्रीन से वर्ल्ड स्पेस में बस जाने के लिए। यह आमतौर पर ऑब्जेक्ट पिकिंग के लिए दुनिया में माउस का स्थान प्राप्त करने के लिए उपयोग किया जाता है।

Vector2.Transform(mouseLocation, Matrix.Invert(Camera.TransformMatrix));

दुनिया से स्क्रीन स्पेस में जाने के लिए बस इसके विपरीत करें।

Vector2.Transform(mouseLocation, Camera.TransformMatrix);

एक मैट्रिक्स का उपयोग करने के अलावा कोई अन्य चीज़ नहीं है, क्योंकि यह थोड़ा सीखना है।

दृश्य क्षेत्र प्राप्त करना आसान है

public Rectangle VisibleArea {
    get {
        var inverseViewMatrix = Matrix.Invert(View);
        var tl = Vector2.Transform(Vector2.Zero, inverseViewMatrix);
        var tr = Vector2.Transform(new Vector2(_screenSize.X, 0), inverseViewMatrix);
        var bl = Vector2.Transform(new Vector2(0, _screenSize.Y), inverseViewMatrix);
        var br = Vector2.Transform(_screenSize, inverseViewMatrix);
        var min = new Vector2(
            MathHelper.Min(tl.X, MathHelper.Min(tr.X, MathHelper.Min(bl.X, br.X))), 
            MathHelper.Min(tl.Y, MathHelper.Min(tr.Y, MathHelper.Min(bl.Y, br.Y))));
        var max = new Vector2(
            MathHelper.Max(tl.X, MathHelper.Max(tr.X, MathHelper.Max(bl.X, br.X))), 
            MathHelper.Max(tl.Y, MathHelper.Max(tr.Y, MathHelper.Max(bl.Y, br.Y))));
        return new Rectangle((int)min.X, (int)min.Y, (int)(max.X - min.X), (int)(max.Y - min.Y));
    }
}

आप बस कैमरों के कोनों को बदल सकते हैं और विश्व स्थान में अपना स्थान प्राप्त कर सकते हैं। न्यूनतम अधिकतम x, y मान और आप देखने योग्य स्थान के चारों ओर एक आयत प्राप्त कर सकते हैं। कॉल खींचने और अनुकूलन के लिए बहुत उपयोगी है।


1
धन्यवाद। मुझे मोनोगेम में एक कैमरे को लागू करते समय यह मददगार लगा
craftworkgames

6

अपने SpriteBatch पर एक मैट्रिक्स को लागू करना एक बार में पूरे ड्रॉ कॉल को बदल देता है। इसका मतलब है कि आपको अपने ड्रॉइलेट्स विधि में अपने कैमरे का उपयोग करने की आवश्यकता नहीं है।

यह बहुत आसान हो सकता है जैसे:

    // Loop through the number of visible tiles.
    for (int y = 0; y <= tiles.GetUpperBound(1); y++) {
        for (int x = 0; x <= tiles.GetUpperBound(0); x++) {

                // If the tile is not an empty space.
                if (tiles[x, y].Texture != null) {
                     // Get the position of the visible tile within the viewport by multiplying the counters by the tile dimensions
                     // and subtracting the camera offset values incase the position of the camera means only part of a tile is visible.
                     Vector2 tilePosition = new Vector2(x * Tile.Width, y * Tile.Height);
                     // Draw the correct tile
                     spriteBatch.Draw(tiles[x, y].Texture, tilePosition, Color.White);
                }
        }
    }

तो एक मैट्रिक्स का उपयोग करने की बात यह है कि आपको इसके बारे में सोचने की ज़रूरत नहीं है। बस चीजों को आकर्षित करें और स्वतंत्र रूप से कैमरे को चारों ओर ले जाएं।

साथ ही, आपका MoveCamera तरीका थोड़ा अजीब लगता है। कैमरा क्लास होना बहुत ही असामान्य बात है जो एक स्तर को एक निर्भरता के रूप में लेता है। एक अधिक विशिष्ट कार्यान्वयन इस तरह दिखेगा:

public void MoveCamera(float deltaX, float deltaY) {
    mCameraPosition.X += deltaX;
    mCameraPosition.Y += deltaY;
}

फिर अपने अद्यतन विधि में आप ऐसा कुछ कर सकते हैं:

    if (Direction == ScrollDirection.Up)
    {
        mCamera.MoveCamera(mScrollSpeed.Y, 0);
    }

कुल मिलाकर, मेरा सुझाव इसे सरल रखना है। इसे सरलतम तरीके से काम करते हुए प्राप्त करें और इस पर निर्माण करें। जब तक आपको पहले काम करने वाली मूल बातें नहीं मिल जाती हैं तब तक अत्यधिक अनुकूलित कोड न लिखने का प्रयास करें। आप पा सकते हैं कि हर टाइल हर फ्रेम प्रदान करना इतना बुरा नहीं है।

EDIT: प्रश्न के दूसरे भाग के लिए।

हालांकि यह सच है कि आप अपने बैच की गिनती कम रखना चाहते हैं, 2 या 3 का होना कोई समस्या नहीं होनी चाहिए। तो अगर आपके पास दूसरा स्प्राइट बैच बनाने का एक अच्छा कारण है तो बस इसे करें।

उस ने कहा, इस मामले में दूसरे स्प्राइट बैच का उपयोग करने का एक अच्छा कारण नहीं है। अधिक संभावना है, आप अपने खिलाड़ी को ठीक उसी तरह से खींचना चाहते हैं जिस तरह से आप एक ही स्प्राइट बैच में टाइल लगाते हैं, जिस पर कैमरा ट्रांसफ़ॉर्म किया जाता है।

यह बताना थोड़ा कठिन है कि आपका खिलाड़ी बिना किसी कोड को देखे क्यों पीछे छूट रहा है, लेकिन यह इस कारण से है कि यदि आप अपने खिलाड़ी को टाइल की तरह ठीक उसी स्थिति में खींचते हैं, तो वह उसी स्थिति में दिखाई देगा स्प्राइट बैच।

उदाहरण के लिए, यदि आप चाहते हैं कि खिलाड़ी टाइल 10, 10 पर दिखाई दे, तो आप ऐसा कर सकते हैं:

var playerPosition = new Vector2(10 * Tile.Width, 10 * Tile.Height);
spriteBatch.Draw(player.Texture, playerPosition, Color.White);

चीजों को आरेखित करने के बारे में सोचने की मानसिकता में आने की कोशिश करें जहां वे हैं और कैमरा सचमुच पूरे "दृश्य" को दृश्य में ले जाता है। यही आपका मैट्रिक्स रूपांतरण कर रहा है।


यह वास्तव में मददगार है और चीजों को साफ करता है! जानकारी के लिए धन्यवाद; बहुत सराहना की। मैं आज आपकी सलाह को बाद में लागू करने का प्रयास करूंगा और आपको बताऊंगा कि मुझे कैसे प्राप्त होता है। एक बार फिर धन्यवाद।
पेक्टस एक्सावटम

इसके अलावा, बस ब्याज से बाहर है, क्या व्यूपोर्ट में केवल टाइल खींचने के लिए मैट्रिक्स परिवर्तन का उपयोग करने का एक तरीका है?
पेक्टस एक्वासटम

ऐसा करने का एक तरीका है, लेकिन मुझे अपने सिर के ऊपर से पता नहीं है। मुझे लगता है कि यह व्यूपोर्ट आयत का उपयोग करने के साथ कुछ करना होगा, शायद कैमरे को बदलने के बाद इसे लागू करें। मुझे पता नहीं, आपको प्रयोग करना होगा। अपने डिबगर का उपयोग करें।
craftworkgames

ठीक है धन्यवाद। मैंने इसके साथ खेला है और कहीं मिला है, लेकिन खिलाड़ी के साथ क्या करना है, इस बारे में एक सवाल में भाग लिया है - कृपया प्रश्न पर संपादन देखें।
पेक्टस एक्वासटम

धन्यवाद, मैंने आपकी सलाह ली और उसी स्प्राइट बैच का उपयोग करने के विकल्प के साथ चला गया और बस अपडेट में कैमरा स्थिति प्राप्त करना और ड्राइंग करते समय इसे खिलाड़ियों की स्थिति में लागू करना। अच्छा काम करने लगता है। आपकी सभी मदद के लिए धन्यवाद, थोड़ी देर के लिए कैमरे पर अटक गया। अब बहुत स्पष्ट है :-)
पेक्टस एक्वासटम
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.