অনেক ছোট কোলাইডারকে বৃহত্তর লোকের সাথে সংযুক্ত করা


13

আমি কয়েক হাজার গ্রিড স্কোয়ার দিয়ে তৈরি টাইল-মানচিত্র ব্যবহার করে একটি গেম তৈরি করছি। এই মুহুর্তে, প্রতিটি স্কোয়ারের সংঘর্ষগুলি পরীক্ষা করার জন্য এটিতে একটি বর্গাকার সংঘর্ষক রয়েছে।

এখানে চিত্র বর্ণনা লিখুন

যাইহোক, হাজার হাজার ক্ষুদ্র ক্ষুদ্র ব্লক সহ, সংঘর্ষের জন্য তাদের সমস্ত পরীক্ষা করা কার্যকর নয়। যদি আমি জানতাম যে টাইলম্যাপটি আগে থেকেই দেখতে পাচ্ছিল তবে আমি হাজার হাজার ক্ষুদ্র লোকের চেয়ে 3 বা 4 টি বড় সংঘর্ষকারীকে ব্যবহার করতে পারতাম:

এখানে চিত্র বর্ণনা লিখুন

অনেকগুলি ছোট সংলগ্ন টাইলসকে সর্বাধিক বৃহত আকারের সাথে সংযুক্ত করার জন্য কি কিছু ধরণের স্ট্যান্ডার্ড অ্যালগরিদম রয়েছে? যদি তা হয় তবে কেউ কি এখানে এটি বর্ণনা করতে পারে, বা এই জাতীয় অ্যালগরিদমে সাহিত্যের দিকে নির্দেশ করতে পারে?

বিকল্পভাবে, সম্ভবত টাইল সংঘর্ষকারীদের প্রাক-প্রক্রিয়াজাতকরণ সম্পূর্ণ ভুল পদ্ধতি। যদি তা হয় তবে অত্যন্ত সংখ্যক সংঘর্ষকারীদের দক্ষতার সাথে মোকাবিলা করার জন্য সঠিক কোনটি?


আপনি কি ভূখণ্ডকে ধ্বংসাত্মক করার পরিকল্পনা করছেন?
জাগল্যান্ট

@Jon। আমি এই বিবেচনা করা হয়নি। আমি কল্পনা করেছি যে ধ্বংসাত্মকতার অনুমতি দেওয়া সমস্যাটিকে তাত্পর্যপূর্ণ করে তুলবে (কারণ সামান্য সংঘর্ষকারীদের মধ্যে একটি ধ্বংস হয়ে যেতে পারে, যার অর্থ বড় সংযুক্ত সংঘর্ষকারীদের পুনরায় গণনা করা দরকার, তাই না?)
ক্রেগ ইনস

হ্যাঁ. এই কারণেই আমি জিজ্ঞাসা করছিলাম। সাধারণত, আপনি আপনার সমস্ত অঞ্চলকে জালের সাথে একত্রিত করবেন। যদি আপনি আপনার অঞ্চলটিকে ধ্বংসাত্মক হতে দেওয়ার পরিকল্পনা করেন, তবে এমন একটি বিকল্প পদ্ধতি রয়েছে যা আপনি ব্যবহার করতে পারেন, যা কেবল বাইরের ব্লকে কলকাইডার সেট করে। আপনি ব্লকগুলি "প্রান্তের ব্লক" কী তা প্রাক-ক্যালক করেন এবং তারপরে সেই ব্লকগুলিকে একটি পোলিয়েবল কলসিডার দিয়েছিলেন। ( jgallant.com/images/uranus/chunk.png - চিত্রটি পুরানো এবং নিখুঁত নয়, তবে কৌশলটি দেখায়) গেম ইঞ্জিন / প্ল্যাটফর্মের জন্য আপনি কী ব্যবহার করছেন?
জাগল্যান্ট

@ জন আমি ইউনিটিটি আমার গেম ইঞ্জিন হিসাবে টাইলের সংঘর্ষের জন্য বক্সক্লাইডার 2 ডি উপাদান সহ ব্যবহার করছি। আমি আমার নির্দিষ্ট প্ল্যাটফর্মটি উল্লেখ করিনি কারণ আমি ভেবেছিলাম যে এই সমস্যার আরও সাধারণ উত্তর পেতে গেম ডে স্ট্যাক এক্সচেঞ্জের এটি আরও বেশি ব্যবহার হতে পারে। আপনার "এজ ব্লকস" পদ্ধতির প্রতি, আপনি কি এই পদ্ধতির জন্য অ্যালগোরিদমের সুনির্দিষ্ট বিবরণ সহ উত্তর জমা দিতে পারেন? বা এই জাতীয় কৌশলগুলি সম্পর্কে আপনার কি সংস্থান আছে?
ক্রেগ ইনস

1
আমার এটির জন্য Unক্য বাস্তবায়ন আছে, এটি লেখার সময় নিতে আমার কিছুটা সময় লাগবে, কারণ এটি সত্যিই কাটা এবং শুকনো নয়। আমি এই মুহুর্তে কাজ করছি, এবং উত্স কোড বাড়িতে আছে home আপনি যদি কোনও উত্তরটির জন্য আজ রাত পর্যন্ত অপেক্ষা করতে পারেন। এটি দেখতে কেমন দেখাচ্ছে তা এখানে: jgallant.com/images/landgen.gif
জাগল্যান্ট

উত্তর:


5

আমি love2d ইঞ্জিন ( লুয়া ভাষা ) এর জন্য এই অ্যালগরিটামটি পুরোপুরি উপকার পেয়েছি

https://love2d.org/wiki/TileMerging

-- map_width and map_height are the dimensions of the map
-- is_wall_f checks if a tile is a wall

local rectangles = {} -- Each rectangle covers a grid of wall tiles

for x = 0, map_width - 1 do
    local start_y
    local end_y

    for y = 0, map_height - 1 do
        if is_wall_f(x, y) then
            if not start_y then
                start_y = y
            end
            end_y = y
        elseif start_y then
            local overlaps = {}
            for _, r in ipairs(rectangles) do
                if (r.end_x == x - 1)
                  and (start_y <= r.start_y)
                  and (end_y >= r.end_y) then
                    table.insert(overlaps, r)
                end
            end
            table.sort(
                overlaps,
                function (a, b)
                    return a.start_y < b.start_y
                end
            )

            for _, r in ipairs(overlaps) do
                if start_y < r.start_y then
                    local new_rect = {
                        start_x = x,
                        start_y = start_y,
                        end_x = x,
                        end_y = r.start_y - 1
                    }
                    table.insert(rectangles, new_rect)
                    start_y = r.start_y
                end

                if start_y == r.start_y then
                    r.end_x = r.end_x + 1

                    if end_y == r.end_y then
                        start_y = nil
                        end_y = nil
                    elseif end_y > r.end_y then
                        start_y = r.end_y + 1
                    end
                end
            end

            if start_y then
                local new_rect = {
                    start_x = x,
                    start_y = start_y,
                    end_x = x,
                    end_y = end_y
                }
                table.insert(rectangles, new_rect)

                start_y = nil
                end_y = nil
            end
        end
    end

    if start_y then
        local new_rect = {
            start_x = x,
            start_y = start_y,
            end_x = x,
            end_y = end_y
        }
        table.insert(rectangles, new_rect)

        start_y = nil
        end_y = nil
    end
end
Here's how the rectangles would be used for physics.
-- Use contents of rectangles to create physics bodies
-- phys_world is the world, wall_rects is the list of...
-- wall rectangles

for _, r in ipairs(rectangles) do
    local start_x = r.start_x * TILE_SIZE
    local start_y = r.start_y * TILE_SIZE
    local width = (r.end_x - r.start_x + 1) * TILE_SIZE
    local height = (r.end_y - r.start_y + 1) * TILE_SIZE

    local x = start_x + (width / 2)
    local y = start_y + (height / 2)

    local body = love.physics.newBody(phys_world, x, y, 0, 0)
    local shape = love.physics.newRectangleShape(body, 0, 0,
      width, height)

    shape:setFriction(0)

    table.insert(wall_rects, {body = body, shape = shape})
end

এখানে আমার বর্তমান প্রকল্পের love2d উদাহরণ অনুসরণ করুন। লাল আপনি আমার প্রাচীর সংঘর্ষকারী দেখতে পাবেন।

এখানে চিত্র বর্ণনা লিখুন


একটি সি # সংস্করণ আছে? ডকুমেন্টেশন মন্তব্য সহ একটি সংস্করণ আছে? এই অ্যালগরিদম 3 ডি জন্য অভিযোজিত হতে পারে?
অ্যারন ফ্র্যাঙ্ক

3

আপনি যদি ধ্বংসাত্মক ভূখণ্ড তৈরি করতে চাইছেন তবে আমি যেভাবে Unক্যে এটি করেছি তা হ'ল কেবলমাত্র আপনার বিশ্বের প্রান্তে ব্লকগুলিকে সেট করা। সুতরাং উদাহরণস্বরূপ, আপনি এটি করতে চান এটি:

সবুজ ব্লকগুলি এমন টাইলগুলি নির্দেশ করে যা একটি সংঘর্ষক রয়েছে

এই সবুজ ব্লকগুলিতে একটি সংঘর্ষক রয়েছে, এবং বাকী সমস্তর মধ্যে এটি নেই। এটি গণনার উপর একটি টন সাশ্রয় করে। আপনি যদি কোনও ব্লক ধ্বংস করেন, আপনি খুব সহজেই সংলগ্ন ব্লকগুলিতে সংঘর্ষকারীদের সক্রিয় করতে পারেন। মনে রাখবেন যে কোনও সংঘর্ষককে সক্রিয় / নিষ্ক্রিয় করা ব্যয়বহুল এবং অল্প পরিমাণে করা উচিত।

সুতরাং, টাইল রিসোর্সটি দেখে মনে হচ্ছে:

টালি রিসোর্স ইন ইউনিটি

এটি একটি স্ট্যান্ডার্ড গেমোবজেক্ট তবে এটি পোলযোগ্যও। এছাড়াও লক্ষ্য করুন যে বাক্সের সংঘর্ষক ডিফল্টরূপে অক্ষম করা রয়েছে is আমরা কেবল তখনই এটি সক্রিয় করব যদি এটি একটি প্রান্ত টাইল হয়।

আপনি যদি বিশ্বজুড়ে স্থিতিশীলভাবে লোড করছেন, আপনার টাইলগুলি পুল করার দরকার নেই। আপনি এগুলি কেবল একটি শটে লোড করতে পারেন, প্রান্ত থেকে তাদের দূরত্ব গণনা করতে পারেন এবং প্রয়োজনে একটি সংঘর্ষক প্রয়োগ করতে পারেন।

আপনি যদি গতিশীলভাবে লোড করছেন তবে একটি টাইল পুল ব্যবহার করা ভাল। এখানে আমার রিফ্রেশ লুপের সম্পাদিত উদাহরণ। এটি বর্তমান ক্যামেরা ভিউয়ের উপর ভিত্তি করে টাইলগুলি লোড করে:

public void Refresh(Rect view)
{       
    //Each Tile in the world uses 1 Unity Unit
    //Based on the passed in Rect, we calc the start and end X/Y values of the tiles presently on screen        
    int startx = view.x < 0 ? (int)(view.x + (-view.x % (1)) - 1) : (int)(view.x - (view.x % (1)));
    int starty = view.y < 0 ? (int)(view.y + (-view.y % (1)) - 1) : (int)(view.y - (view.y % (1)));

    int endx = startx + (int)(view.width);
    int endy = starty - (int)(view.height);

    int width = endx - startx;
    int height = starty - endy;

    //Create a disposable hashset to store the tiles that are currently in view
    HashSet<Tile> InCurrentView = new HashSet<Tile>();

    //Loop through all the visible tiles
    for (int i = startx; i <= endx; i += 1)
    {
        for (int j = starty; j >= endy; j -= 1)
        {
            int x = i - startx;
            int y = starty - j;

            if (j > 0 && j < Height)
            {
                //Get Tile (I wrap my world, that is why I have this mod here)
                Tile tile = Blocks[Helper.mod(i, Width), j];

                //Add tile to the current view
                InCurrentView.Add(tile);

                //Load tile if needed
                if (!tile.Blank)
                {
                    if (!LoadedTiles.Contains(tile))
                    {                           
                        if (TilePool.AvailableCount > 0)
                        {
                            //Grab a tile from the pool
                            Pool<PoolableGameObject>.Node node = TilePool.Get();

                            //Disable the collider if we are not at the edge
                            if (tile.EdgeDistance != 1)
                                node.Item.GO.GetComponent<BoxCollider2D>().enabled = false;

                            //Update tile rendering details
                            node.Item.Set(tile, new Vector2(i, j), DirtSprites[tile.TextureID], tile.Collidable, tile.Blank);
                            tile.PoolableGameObject = node;
                            node.Item.Refresh(tile);

                            //Tile is now loaded, add to LoadedTiles hashset
                            LoadedTiles.Add(tile);

                            //if Tile is edge block, then we enable the collider
                            if (tile.Collidable && tile.EdgeDistance == 1)
                                node.Item.GO.GetComponent<BoxCollider2D>().enabled = true;
                        }
                    }                       
                }                  
            }
        }
    }

    //Get a list of tiles that are no longer in the view
    HashSet<Tile> ToRemove = new HashSet<Tile>();
    foreach (Tile tile in LoadedTiles)
    {
        if (!InCurrentView.Contains(tile))
        {
            ToRemove.Add(tile);
        }
    }

    //Return these tiles to the Pool 
    //this would be the simplest form of cleanup -- Ideally you would do this based on the distance of the tile from the viewport
    foreach (Tile tile in ToRemove)
    {
        LoadedTiles.Remove(tile);
        tile.PoolableGameObject.Item.GO.GetComponent<BoxCollider2D>().enabled = false;
        tile.PoolableGameObject.Item.GO.transform.position = new Vector2(Int32.MinValue, Int32.MinValue);
        TilePool.Return(tile.PoolableGameObject);            
    }

    LastView = view;
}

আদর্শভাবে, আমি আরও অনেক বিস্তারিত পোস্ট লিখব, কারণ পর্দার পিছনে আরও কিছুটা চলছে। তবে এটি আপনাকে সাহায্য করতে পারে। যদি প্রশ্ন থাকে তবে নির্দ্বিধায় আমাকে জিজ্ঞাসা করতে বা যোগাযোগ করতে পারেন।


ডিএনকেড্রনের উত্তর স্বীকার করা হয়েছে কারণ এটি উত্থাপিত মূল প্রশ্নের আরও সরাসরি উত্তর দেয়। তবে এটি একটি দক্ষ বিকল্পের পক্ষে মূল্যবান দিকনির্দেশ হিসাবে এই উত্তরটিকে অগ্রাহ্য করেছে
ক্রেগ ইনেস

@ ক্রেইগইননেস কোন সমস্যা নেই। শুধু সাহায্য করতে চান। পয়েন্টগুলি কোনও বিষয় নয় :)
জাগল্যান্ট
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.