मैं सरणी आधारित हेक्स मानचित्र पर हेक्स समन्वय के लिए पिक्सेल कैसे प्राप्त करूं?


10

मैं एक हेक्स मैप के लिए फंक्शन कोऑर्ड करने के लिए एक पिक्सेल बनाने की कोशिश कर रहा हूं, लेकिन मुझे गणित सही ढंग से नहीं मिल रहा है, मैं जो भी कोशिश करता हूं वह थोड़ा बंद हो जाता है, और मैंने जो उदाहरण पाया है वह सर्किल केंद्रित मानचित्रों पर आधारित थे।

'सरणी आधारित' से मेरा मतलब है कि जिस तरह से हेक्स का आदेश दिया गया है, उसे देखें।

मुझे मिला सबसे सटीक परिणाम निम्नलिखित कोड के साथ था, लेकिन यह अभी भी बंद है, और इससे अधिक मूल्य बढ़ जाता है:

public HexCell<T> coordsToHexCell(float x, float y){
    final float size = this.size; // cell size
    float q = (float) ((1f/3f* Math.sqrt(3) * x - 1f/3f * y) / size);
    float r = 2f/3f * y / size;
    return getHexCell((int) r, (int) q);
}

Hexmap

शीर्ष 0 पर स्क्रीन 0,0 से शुरू होती है, प्रत्येक सेल को इसका केंद्र पता होता है।

मुझे केवल हेक्स निर्देशांक में स्क्रीन निर्देशांक का अनुवाद करने की आवश्यकता है। ऐसा कैसे किया जा सकता था?

जवाबों:


12

कई हेक्स समन्वय प्रणाली हैं। एक आयताकार मानचित्र को संवारने के लिए "ऑफ़सेट" दृष्टिकोण अच्छा है, लेकिन हेक्स एल्गोरिदम ट्रिकियर होते हैं।

मेरे में हेक्स ग्रिड गाइड (जो मेरा मानना है कि आप पहले से ही मिल गया है), अपने समन्वय प्रणाली होते हैं, तो लेबलिंग कहा जाता है "भी-r", आप को छोड़कर r,qबजाय q,r। आप इन चरणों के साथ पिक्सेल स्थानों को हेक्स निर्देशांक में बदल सकते हैं:

  1. इस खंड में वर्णित एल्गोरिथ्म का उपयोग करके पिक्सेल स्थानों को अक्षीय हेक्स निर्देशांक में परिवर्तित करें । यह आपका कार्य करता है। हालाँकि, आपको एक और कदम उठाने की आवश्यकता है।
  2. वे अक्षीय निर्देशांक भिन्नात्मक हैं। उन्हें निकटतम हेक्स से गोल करने की आवश्यकता है। आपके कोड में आप उपयोग करते हैं (int)r, (int)qलेकिन वह केवल वर्गों के लिए काम करता है; हेक्स के लिए हमें एक अधिक जटिल गोलाई दृष्टिकोण की आवश्यकता है। कन्वर्ट r, qकरने के लिए घन का उपयोग कर निर्देशांक घन के अक्षीय सूत्रों यहाँ । फिर यहांhex_round फ़ंक्शन का उपयोग करें
  3. अब आपके पास घन निर्देशांक का पूर्णांक सेट है । आपका मानचित्र "सम-आर" का उपयोग करता है, घन का नहीं, इसलिए आपको वापस परिवर्तित करने की आवश्यकता है। यहाँ से भी- सूत्र ऑफ़सेट के लिए क्यूब का उपयोग करें

मुझे पिक्सेल को हेक्स समन्वय अनुभाग में फिर से लिखने के लिए इसे और अधिक स्पष्ट करने की आवश्यकता है। माफ़ करना!

मुझे पता है, यह जटिल लगता है। मैं इस दृष्टिकोण का उपयोग करता हूं क्योंकि यह कम से कम त्रुटि वाला है (कोई विशेष मामले नहीं!) और पुन: उपयोग के लिए अनुमति देता है। उन रूपांतरण रूटीन का पुन: उपयोग किया जा सकता है। हेक्स राउंडिंग का पुन: उपयोग किया जा सकता है। यदि आप कभी भी रेखाओं को खींचना चाहते हैं या एक हेक्स समन्वय के चारों ओर घूमना चाहते हैं या दृश्य या अन्य एल्गोरिदम का क्षेत्र करना चाहते हैं, तो इनमें से कुछ रूटीन वहां भी उपयोगी होंगे।


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

2
@amitp: मुझे आपके गाइड से प्यार है, मैंने उस पर ठोकर खाई जब मैंने कुछ साल पहले एक हेक्सागोनल ग्रिड जनरेटर लिखा था। यदि आप रुचि रखते हैं तो यहां मेरा समाधान है: स्टैक ओवरफ्लो - समन्वय प्रणाली के साथ एक हेक्सागोनल ग्रिड उत्पन्न करने के लिए एल्गोरिदम
श्री पॉलीविरल

1
पिक्सेल निर्देशांक की उत्पत्ति कहाँ है? ऑफसेट निर्देशांक में हेक्सागोन 0,0 के केंद्र में?
एंड्रयू

1
@ और हां। हेक्स निर्देशांक में परिवर्तन चलाने से पहले आप पिक्सेल निर्देशांक में मूल को स्थानांतरित कर सकते हैं।
१p

9

इस समस्या को संभालने के दो तरीके हैं, मेरी राय में।

  1. एक बेहतर समन्वय प्रणाली का उपयोग करें। आप अपने आप पर गणित को बहुत आसान बना सकते हैं यदि आप चालाक हैं कि आप हेक्स को कैसे संख्या देते हैं। हेक्सागोनल ग्रिड पर अमित पटेल का निश्चित संदर्भ है । आप उस पृष्ठ पर अक्षीय निर्देशांक देखना चाहते हैं ।

  2. जो पहले से ही इसे हल किया है से उधार कोड। मेरे पास कुछ कोड है जो काम करता है, जिसे मैंने बैटल फॉर वेसनॉथ स्रोत से उठाया था । ध्यान रखें कि मेरे संस्करण में शीर्ष पर हेक्स का सपाट हिस्सा है, इसलिए आपको एक्स और वाई स्वैप करना होगा।


6

मुझे लगता है कि माइकल क्रिस्टोफ़िक का जवाब सही है, विशेष रूप से अमित पटेल की वेबसाइट का उल्लेख करने के लिए, लेकिन मैं हेक्स ग्रिड के लिए अपने नौसिखिए दृष्टिकोण को साझा करना चाहता था।

यह कोड लिया गया था एक ऐसी परियोजना से जिसमें मैंने जावास्क्रिप्ट में लिखित रुचि को छोड़ दिया और छोड़ दिया , लेकिन हेक्स टाइल के लिए माउस की स्थिति ने बहुत अच्छा काम किया। मैंने अपने संदर्भों के लिए * इस GameDev लेख * का उपयोग किया । उस वेबसाइट से लेखक की यह छवि थी जिसने दिखाया कि कैसे सभी हेक्स पक्षों और स्थितियों का गणितीय रूप से प्रतिनिधित्व करते हैं।

मेरे रेंडर क्लास में मैंने इसे एक विधि में परिभाषित किया था जिससे मुझे किसी भी हेक्स साइड लंबाई को सेट करने की अनुमति मिली जो मैं चाहता था। यहां दिखाया गया है क्योंकि इनमें से कुछ मूल्यों को पिक्सेल में हेक्स समन्वय कोड में संदर्भित किया गया था।

                this.s = Side; //Side length
                this.h = Math.floor(Math.sin(30 * Math.PI / 180) * this.s);
                this.r = Math.floor(Math.cos(30 * Math.PI / 180) * this.s);
                this.HEXWIDTH = 2 * this.r;
                this.HEXHEIGHT = this.h + this.s;
                this.HEXHEIGHT_CENTER = this.h + Math.floor(this.s / 2);

माउस इनपुट क्लास में, मैंने एक ऐसा तरीका बनाया जो स्क्रीन x और y कोऑर्डिनेट करना स्वीकार करता है, और हेक्स के साथ एक ऑब्जेक्ट लौटाता है जो पिक्सेल के भीतर रहता है। * ध्यान दें कि मेरे पास एक नकली "कैमरा" था ताकि रेंडर पोजीशन के लिए ऑफसेट भी शामिल हो।

    ConvertToHexCoords:function (xpixel, ypixel) {
        var xSection = Math.floor(xpixel / ( this.Renderer.HEXWIDTH )),
            ySection = Math.floor(ypixel / ( this.Renderer.HEXHEIGHT )),
            xSectionPixel = Math.floor(xpixel % ( this.Renderer.HEXWIDTH )),
            ySectionPixel = Math.floor(ypixel % ( this.Renderer.HEXHEIGHT )),
            m = this.Renderer.h / this.Renderer.r, //slope of Hex points
            ArrayX = xSection,
            ArrayY = ySection,
            SectionType = 'A';
        if (ySection % 2 == 0) {
            /******************
             * http://www.gamedev.net/page/resources/_/technical/game-programming/coordinates-in-hexagon-based-tile-maps-r1800
             * Type A Section
             *************
             *     *     *
             *   *   *   *
             * *       * *
             * *       * *
             *************
             * If the pixel position in question lies within the big bottom area the array coordinate of the
             *      tile is the same as the coordinate of our section.
             * If the position lies within the top left edge we have to subtract one from the horizontal (x)
             *      and the vertical (y) component of our section coordinate.
             * If the position lies within the top right edge we reduce only the vertical component.
             ******************/
            if (ySectionPixel < (this.Renderer.h - xSectionPixel * m)) {// left Edge
                ArrayY = ySection - 1;
                ArrayX = xSection - 1;
            } else if (ySectionPixel < (-this.Renderer.h + xSectionPixel * m)) {// right Edge
                ArrayY = ySection - 1;
                ArrayX = xSection;
            }
        } else {
            /******************
             * Type B section
             *********
             * *   * *
             *   *   *
             *   *   *
             *********
             * If the pixel position in question lies within the right area the array coordinate of the
             *      tile is the same as the coordinate of our section.
             * If the position lies within the left area we have to subtract one from the horizontal (x) component
             *      of our section coordinate.
             * If the position lies within the top area we have to subtract one from the vertical (y) component.
             ******************/
            SectionType = 'B';
            if (xSectionPixel >= this.Renderer.r) {//Right side
                if (ySectionPixel < (2 * this.Renderer.h - xSectionPixel * m)) {
                    ArrayY = ySection - 1;
                    ArrayX = xSection;
                } else {
                    ArrayY = ySection;
                    ArrayX = xSection;
                }
            } else {//Left side
                if (ySectionPixel < ( xSectionPixel * m)) {
                    ArrayY = ySection - 1;
                    ArrayX = xSection;
                } else {
                    ArrayY = ySection;
                    ArrayX = xSection - 1;
                }
            }
        }
        return {
            x:ArrayX + this.Main.DrawPosition.x, //Draw position is the "camera" offset
            y:ArrayY + this.Main.DrawPosition.y
        };
    },

अंत में यहाँ रेंडर डिबग के साथ मेरे प्रोजेक्ट का स्क्रीनशॉट है। यह लाल रेखाओं को दिखाता है जहां हेक्स निर्देशांक और सेल रूपरेखा के साथ टाइपा बनाम टाइपबी कोशिकाओं के लिए कोड की जांच करता है यहाँ छवि विवरण दर्ज करें
आशा है कि यह कुछ मदद करता है।


4

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

public HexCell<T> coordsToHexCell(float x, float y){
    HexCell<T> cell;
    HexCell<T> result = null;
    float distance = Float.MAX_VALUE;
    for (int r = 0; r < rows; r++) {
        for (int c = 0; c < cols; c++) {
            cell = getHexCell(r, c);

            final float dx = x - cell.getX();
            final float dy = y - cell.getY();
            final float newdistance = (float) Math.sqrt(dx*dx + dy*dy);

            if (newdistance < distance) {
                distance = newdistance;
                result = cell;
            }           
        }
    }
    return result;
}

3
यह एक उचित दृष्टिकोण है। आप सभी को स्कैन करने के बजाय पंक्तियों / कॉलों की एक छोटी श्रृंखला को स्कैन करके इसे गति दे सकते हैं। ऐसा करने के लिए आपको एक कठिन विचार की आवश्यकता है जहां हेक्स है। चूंकि आप ऑफ़सेट ग्रिड का उपयोग कर रहे हैं, आप स्तंभों के बीच अंतर द्वारा x को विभाजित करके और पंक्तियों द्वारा अंतर y को विभाजित करके एक मोटा अनुमान प्राप्त कर सकते हैं। फिर सभी कॉलम 0…cols-1और सभी पंक्तियों 0…rows-1को स्कैन करने के बजाय , आप स्कैन कर सकते हैं col_guess - 1 … col_guess+1और row_guess - 1 … row_guess + 1। यह केवल 9 हेक्स है इसलिए यह तेज़ है और नक्शे के आकार पर निर्भर नहीं है।
amitp

3

यहां अमित पटेल की वेब-साइट पर पोस्ट की गई तकनीकों में से एक # के कार्यान्वयन की हिम्मत है (मुझे यकीन है कि जावा में अनुवाद करना एक चुनौती नहीं होगी):

public class Hexgrid : IHexgrid {
  /// <summary>Return a new instance of <c>Hexgrid</c>.</summary>
  public Hexgrid(IHexgridHost host) { Host = host; }

  /// <inheritdoc/>
  public virtual Point ScrollPosition { get { return Host.ScrollPosition; } }

/// <inheritdoc/>
public virtual Size  Size           { get { return Size.Ceiling(Host.MapSizePixels.Scale(Host.MapScale)); } }

/// <inheritdoc/>
public virtual HexCoords GetHexCoords(Point point, Size autoScroll) {
  if( Host == null ) return HexCoords.EmptyCanon;

  // Adjust for origin not as assumed by GetCoordinate().
  var grid    = new Size((int)(Host.GridSizeF.Width*2F/3F), (int)Host.GridSizeF.Height);
  var margin  = new Size((int)(Host.MapMargin.Width  * Host.MapScale), 
                         (int)(Host.MapMargin.Height * Host.MapScale));
  point      -= autoScroll + margin + grid;

  return HexCoords.NewCanonCoords( GetCoordinate(matrixX, point), 
                                   GetCoordinate(matrixY, point) );
}

/// <inheritdoc/>
public virtual Point   ScrollPositionToCenterOnHex(HexCoords coordsNewCenterHex) {
  return HexCenterPoint(HexCoords.NewUserCoords(
          coordsNewCenterHex.User - ( new IntVector2D(Host.VisibleRectangle.Size.User) / 2 )
  ));
}

/// <summary>Scrolling control hosting this HexGrid.</summary>
protected IHexgridHost Host { get; private set; }

/// <summary>Matrix2D for 'picking' the <B>X</B> hex coordinate</summary>
Matrix matrixX { 
  get { return new Matrix(
      (3.0F/2.0F)/Host.GridSizeF.Width,  (3.0F/2.0F)/Host.GridSizeF.Width,
             1.0F/Host.GridSizeF.Height,       -1.0F/Host.GridSizeF.Height,  -0.5F,-0.5F); } 
}
/// <summary>Matrix2D for 'picking' the <B>Y</B> hex coordinate</summary>
Matrix matrixY { 
  get { return new Matrix(
            0.0F,                        (3.0F/2.0F)/Host.GridSizeF.Width,
            2.0F/Host.GridSizeF.Height,         1.0F/Host.GridSizeF.Height,  -0.5F,-0.5F); } 
}

/// <summary>Calculates a (canonical X or Y) grid-coordinate for a point, from the supplied 'picking' matrix.</summary>
/// <param name="matrix">The 'picking' matrix</param>
/// <param name="point">The screen point identifying the hex to be 'picked'.</param>
/// <returns>A (canonical X or Y) grid coordinate of the 'picked' hex.</returns>
  static int GetCoordinate (Matrix matrix, Point point){
  var pts = new Point[] {point};
  matrix.TransformPoints(pts);
      return (int) Math.Floor( (pts[0].X + pts[0].Y + 2F) / 3F );
  }

बाकी परियोजना यहां ओपन सोर्स के रूप में उपलब्ध है, जिसमें मैट्रिक्सइंटेंट 2 डी और वेक्टरआईएनटी 2 डी कक्षाएं शामिल हैं:
http://hexgridutilities.codeplex.com/

यद्यपि ऊपर का कार्यान्वयन फ्लैट-टॉप टॉप हेक्स के लिए है, हेक्सग्रिड यूटिलिटीज लाइब्रेरी में ग्रिड को स्थानांतरित करने का विकल्प शामिल है।


0

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

यह दृष्टिकोण कैटन जैसे खेलों के लिए अच्छी तरह से काम करता है, जहां खिलाड़ी टाइल और कोने के साथ बातचीत करते हैं, लेकिन उन खेलों के लिए अनुकूल नहीं है जहां खिलाड़ी केवल टाइल के साथ बातचीत करते हैं, क्योंकि यह रिटर्न देता है कि कौन से केंद्र बिंदु या शीर्ष निर्देशांक निकटतम हैं, बजाय हेक्सागोनल टाइल। निर्देशांक भीतर हैं।

ज्यामिति

यदि आप किसी ऐसे स्तंभ में बिंदु रखते हैं, जो किसी टाइल की चौड़ाई से चौथाई है, और पंक्तियाँ जो टाइल की ऊँचाई से आधी हैं, तो आपको यह पैटर्न मिलेगा:

जैसा ऊपर बताया गया है

यदि आप एक चेकबोर्ड पैटर्न (छोड़ें if column % 2 + row % 2 == 1) में हर दूसरे डॉट को छोड़ने के लिए कोड को संशोधित करते हैं , तो आप इस पैटर्न के साथ समाप्त होते हैं:

जैसा ऊपर बताया गया है

कार्यान्वयन

उस ज्यामिति को ध्यान में रखते हुए, आप एक 2 डी सरणी बना सकते हैं (जैसे आप एक वर्ग ग्रिड के साथ करेंगे), ग्रिड में x, yप्रत्येक बिंदु के लिए निर्देशांक को संग्रहीत करना (पहले आरेख से) - ऐसा कुछ:

points = []
for x in numberOfColumns
    points.push([])
    for y in numberOfRows
        points[x].push({x: x * widthOfColumn, y: y * heightOfRow})

नोट: जब आप बिंदुओं के चारों ओर एक ग्रिड बना रहे होते हैं, तब सामान्य रूप से, (बिंदुओं पर बिंदुओं को रखने के बजाय ), आपको मूल ऑफसेट करने की आवश्यकता होती है (स्तंभ की आधी चौड़ाई को घटाकर xऔर पंक्ति की आधी ऊंचाई सेy )।

अब जब आप अपने 2 डी सरणी ( points) को आरंभीकृत कर चुके हैं, तो आप माउस को निकटतम बिंदु पा सकते हैं जैसे आप एक वर्ग ग्रिड पर होंगे, केवल दूसरे आरेख में पैटर्न का उत्पादन करने के लिए हर दूसरे बिंदु को अनदेखा करना होगा:

column, row = floor(mouse.x / columnWidth), floor(mouse.y / rowHeight)
point = null if column % 2 + row % 2 != 1 else points[column][row]

यह काम करेगा, लेकिन निर्देशांक निकटतम बिंदु (या कोई बिंदु) पर गोल किए जा रहे हैं, जिसके आधार पर सूचक आयत के भीतर है। आप वास्तव में बिंदु के चारों ओर एक गोलाकार क्षेत्र चाहते हैं (इसलिए स्नैप-रेंज हर दिशा में बराबर है)। अब जब आप जानते हैं कि किस बिंदु को जांचना है, तो आप आसानी से दूरी (पायथागोरस के प्रमेय का उपयोग करके) पा सकते हैं। निहित सर्कल को अभी भी मूल बाउंडिंग आयत के अंदर फिट होना होगा, इसकी अधिकतम व्यास को एक कॉलम की चौड़ाई तक सीमित करना (एक टाइल की चौड़ाई चौथाई), लेकिन यह अभी भी अभ्यास में अच्छी तरह से काम करने के लिए पर्याप्त बड़ा है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.