গতিশীল তরঙ্গ দিয়ে আমি কীভাবে 2 ডি জল তৈরি করব?


81

নতুন সুপার মারিও ব্রোসের কাছে সত্যিই শীতল 2D জল রয়েছে যা আমি কীভাবে তৈরি করব তা শিখতে চাই।

এখানে এটির একটি ভিডিও দেখানো হচ্ছে। একটি উদাহরণস্বরূপ অংশ:

নতুন সুপার মারিও ব্রসের জলের প্রভাব

জলে আঘাতকারী জিনিসগুলি তরঙ্গ তৈরি করে। অবিচ্ছিন্ন "ব্যাকগ্রাউন্ড" তরঙ্গও রয়েছে। ভিডিওতে 00:50 এর ঠিক পরে যখন আপনি ক্যামেরাটি না চালাচ্ছেন তখন ধ্রুবক তরঙ্গগুলিতে আপনি ভাল চেহারা পেতে পারেন।

আমি ধরে নিই যে এই টিউটোরিয়ালের প্রথম অংশের মতো স্প্ল্যাশ এফেক্টস কাজ করে ।

যাইহোক, এনএসএমবিতেও পানির উপরিভাগে ধ্রুবক তরঙ্গ থাকে এবং স্প্ল্যাশগুলি খুব আলাদা দেখায়। আর একটি পার্থক্য হ'ল টিউটোরিয়ালে, যদি আপনি একটি স্প্ল্যাশ তৈরি করেন তবে এটি প্রথমে স্প্ল্যাশের উত্সে জলে গভীর একটি "গর্ত" তৈরি করে। নতুন সুপার মারিও ব্রোসে এই গর্তটি অনুপস্থিত বা আরও ছোট। আমি পানিতে এবং বাইরে লাফানোর সময় প্লেয়ারটি যে স্প্ল্যাশগুলি তৈরি করে তা উল্লেখ করছি।

আমি কীভাবে ধ্রুবক তরঙ্গ এবং স্প্ল্যাশ সহ জলের পৃষ্ঠ তৈরি করব?

আমি এক্সএনএ-তে প্রোগ্রামিং করছি। আমি নিজে এটি চেষ্টা করেছি, তবে ডায়নামিক ওয়েভগুলির সাথে একসাথে ভালভাবে কাজ করার জন্য আমি ব্যাকগ্রাউন্ড সাইন ওয়েভগুলি সত্যিই পেতে পারি না ।

আমি জিজ্ঞাসা করছি না যে নিউ সুপার মারিও ব্রোসের বিকাশকারীরা ঠিক কীভাবে এটি করেছে - ঠিক কীভাবে এটির মতো একটি প্রভাব তৈরি করতে আগ্রহী interested

উত্তর:


147

আমি এটা চেষ্টা করেছি.

স্প্ল্যাশস (স্প্রিংস)

যেহেতু এই টিউটোরিয়ালটিতে উল্লেখ করা হয়েছে, জলের পৃষ্ঠটি তারের মতো: আপনি যদি তারের কোনও বিন্দুতে টানেন তবে সেই বিন্দুর পাশের পয়েন্টগুলিও নীচে টানতে হবে। সমস্ত পয়েন্টগুলিও একটি বেসলাইনে ফিরে আসে।

এটি মূলত একে অপরের পাশে প্রচুর উল্লম্ব ঝর্ণা যা একে অপরকে টান দেয়।

আমি LUVE ব্যবহার করে লুয়ায় এটি স্কেচ করেছি এবং এটি পেয়েছি:

একটি স্প্ল্যাশ অ্যানিমেশন

কল্পনাযোগ্য মনে হচ্ছে। ওহ হুক , তুমি সুদর্শন প্রতিভা।

আপনি যদি এটির সাথে খেলতে চান তবে এখানে একটি জাভাস্ক্রিপ্ট বন্দর সৌজন্যে ফিলের ! আমার কোডটি এই উত্তরের শেষে।

পটভূমি তরঙ্গ (সাইনযুক্ত সাইনস)

প্রাকৃতিক পটভূমি তরঙ্গগুলি আমার দিকে একসাথে সাইন ওয়েভের (যেমন বিভিন্ন প্রশস্ততা, পর্যায় এবং তরঙ্গদৈর্ঘ্য সহ) একত্রে সংক্ষিপ্তসার দেখায়। আমি যখন এটি লিখেছিলাম তখন এখানে দেখতে কেমন দেখাচ্ছে:

সাইন হস্তক্ষেপ দ্বারা উত্পাদিত পটভূমি তরঙ্গ

হস্তক্ষেপ নিদর্শন দেখতে বেশ প্রশ্রয়জনক।

সব একসঙ্গে এখন

সুতরাং স্প্ল্যাশ তরঙ্গ এবং পটভূমি তরঙ্গ একসাথে যোগ করা খুব সহজ বিষয়:

স্প্ল্যাশ সহ পটভূমি তরঙ্গ

যখন স্প্ল্যাশ হয়, আপনি ছোট ধূসর বৃত্তগুলি দেখতে পাবেন যেখানে মূল ব্যাকগ্রাউন্ড তরঙ্গ হবে showing

এটি আপনার লিঙ্ক করা ভিডিওর মতো দেখতে অনেকটা দুর্দান্ত লাগে তাই আমি এটি একটি সফল পরীক্ষা হিসাবে বিবেচনা করব।

এখানে আমার main.lua(একমাত্র ফাইল)। আমি মনে করি এটি বেশ পঠনযোগ্য।

-- Resolution of simulation
NUM_POINTS = 50
-- Width of simulation
WIDTH = 400
-- Spring constant for forces applied by adjacent points
SPRING_CONSTANT = 0.005
-- Sprint constant for force applied to baseline
SPRING_CONSTANT_BASELINE = 0.005
-- Vertical draw offset of simulation
Y_OFFSET = 300
-- Damping to apply to speed changes
DAMPING = 0.98
-- Number of iterations of point-influences-point to do on wave per step
-- (this makes the waves animate faster)
ITERATIONS = 5

-- Make points to go on the wave
function makeWavePoints(numPoints)
    local t = {}
    for n = 1,numPoints do
        -- This represents a point on the wave
        local newPoint = {
            x    = n / numPoints * WIDTH,
            y    = Y_OFFSET,
            spd = {y=0}, -- speed with vertical component zero
            mass = 1
        }
        t[n] = newPoint
    end
    return t
end

-- A phase difference to apply to each sine
offset = 0

NUM_BACKGROUND_WAVES = 7
BACKGROUND_WAVE_MAX_HEIGHT = 5
BACKGROUND_WAVE_COMPRESSION = 1/5
-- Amounts by which a particular sine is offset
sineOffsets = {}
-- Amounts by which a particular sine is amplified
sineAmplitudes = {}
-- Amounts by which a particular sine is stretched
sineStretches = {}
-- Amounts by which a particular sine's offset is multiplied
offsetStretches = {}
-- Set each sine's values to a reasonable random value
for i=1,NUM_BACKGROUND_WAVES do
    table.insert(sineOffsets, -1 + 2*math.random())
    table.insert(sineAmplitudes, math.random()*BACKGROUND_WAVE_MAX_HEIGHT)
    table.insert(sineStretches, math.random()*BACKGROUND_WAVE_COMPRESSION)
    table.insert(offsetStretches, math.random()*BACKGROUND_WAVE_COMPRESSION)
end
-- This function sums together the sines generated above,
-- given an input value x
function overlapSines(x)
    local result = 0
    for i=1,NUM_BACKGROUND_WAVES do
        result = result
            + sineOffsets[i]
            + sineAmplitudes[i] * math.sin(
                x * sineStretches[i] + offset * offsetStretches[i])
    end
    return result
end

wavePoints = makeWavePoints(NUM_POINTS)

-- Update the positions of each wave point
function updateWavePoints(points, dt)
    for i=1,ITERATIONS do
    for n,p in ipairs(points) do
        -- force to apply to this point
        local force = 0

        -- forces caused by the point immediately to the left or the right
        local forceFromLeft, forceFromRight

        if n == 1 then -- wrap to left-to-right
            local dy = points[# points].y - p.y
            forceFromLeft = SPRING_CONSTANT * dy
        else -- normally
            local dy = points[n-1].y - p.y
            forceFromLeft = SPRING_CONSTANT * dy
        end
        if n == # points then -- wrap to right-to-left
            local dy = points[1].y - p.y
            forceFromRight = SPRING_CONSTANT * dy
        else -- normally
            local dy = points[n+1].y - p.y
            forceFromRight = SPRING_CONSTANT * dy
        end

        -- Also apply force toward the baseline
        local dy = Y_OFFSET - p.y
        forceToBaseline = SPRING_CONSTANT_BASELINE * dy

        -- Sum up forces
        force = force + forceFromLeft
        force = force + forceFromRight
        force = force + forceToBaseline

        -- Calculate acceleration
        local acceleration = force / p.mass

        -- Apply acceleration (with damping)
        p.spd.y = DAMPING * p.spd.y + acceleration

        -- Apply speed
        p.y = p.y + p.spd.y
    end
    end
end

-- Callback when updating
function love.update(dt)
    if love.keyboard.isDown"k" then
        offset = offset + 1
    end

    -- On click: Pick nearest point to mouse position
    if love.mouse.isDown("l") then
        local mouseX, mouseY = love.mouse.getPosition()
        local closestPoint = nil
        local closestDistance = nil
        for _,p in ipairs(wavePoints) do
            local distance = math.abs(mouseX-p.x)
            if closestDistance == nil then
                closestPoint = p
                closestDistance = distance
            else
                if distance <= closestDistance then
                    closestPoint = p
                    closestDistance = distance
                end
            end
        end

        closestPoint.y = love.mouse.getY()
    end

    -- Update positions of points
    updateWavePoints(wavePoints, dt)
end

local circle = love.graphics.circle
local line   = love.graphics.line
local color  = love.graphics.setColor
love.graphics.setBackgroundColor(0xff,0xff,0xff)

-- Callback for drawing
function love.draw(dt)

    -- Draw baseline
    color(0xff,0x33,0x33)
    line(0, Y_OFFSET, WIDTH, Y_OFFSET)

    -- Draw "drop line" from cursor

    local mouseX, mouseY = love.mouse.getPosition()
    line(mouseX, 0, mouseX, Y_OFFSET)
    -- Draw click indicator
    if love.mouse.isDown"l" then
        love.graphics.circle("line", mouseX, mouseY, 20)
    end

    -- Draw overlap wave animation indicator
    if love.keyboard.isDown "k" then
        love.graphics.print("Overlap waves PLAY", 10, Y_OFFSET+50)
    else
        love.graphics.print("Overlap waves PAUSED", 10, Y_OFFSET+50)
    end


    -- Draw points and line
    for n,p in ipairs(wavePoints) do
        -- Draw little grey circles for overlap waves
        color(0xaa,0xaa,0xbb)
        circle("line", p.x, Y_OFFSET + overlapSines(p.x), 2)
        -- Draw blue circles for final wave
        color(0x00,0x33,0xbb)
        circle("line", p.x, p.y + overlapSines(p.x), 4)
        -- Draw lines between circles
        if n == 1 then
        else
            local leftPoint = wavePoints[n-1]
            line(leftPoint.x, leftPoint.y + overlapSines(leftPoint.x), p.x, p.y + overlapSines(p.x))
        end
    end
end

দুর্দান্ত উত্তর! আপনাকে অনেক ধন্যবাদ. এবং এছাড়াও, আমার প্রশ্নটি পুনর্বিবেচনার জন্য ধন্যবাদ, আমি দেখতে পাচ্ছি যে এটি আরও স্পষ্ট। এছাড়াও gifs খুব সহায়ক। আপনি কি স্পষ্টত স্প্ল্যাশ তৈরি করার সময় উদয় হওয়া বড় গর্তটিকে রোধ করার কোনও উপায় জানেন? এটি হতে পারে যে মিকেল হগ্রাস্ট্রম ইতিমধ্যে এই অধিকারটির উত্তর দিয়েছিল তবে আমি এই প্রশ্নটি পোস্ট করার আগেও চেষ্টা করেছিলাম এবং আমার ফলাফলটি হ'ল ছিদ্রটি ত্রিভুজাকার আকৃতির হয়ে গেছে এবং এটি খুব অবাস্তব বলে মনে হয়েছিল।
বেরি

"স্প্ল্যাশ হোল" এর গভীরতা কেটে ফেলতে, আপনি তরঙ্গের সর্বাধিক প্রশস্ততা ক্যাপচার করতে পারেন অর্থাৎ বেসলাইন থেকে কোনও বিন্দু কতদূর পথভ্রষ্ট হতে অনুমতি দেয়।
আনকো

3
আগ্রহী প্রত্যেকের জন্য বিটিডব্লিউ: জলের পক্ষগুলি মোড়ানো পরিবর্তে, আমি পক্ষগুলি স্বাভাবিক করার জন্য বেসলাইনটি ব্যবহার করতে পছন্দ করেছি। অন্যথায়, আপনি যদি জলের ডানদিকে একটি স্প্ল্যাশ তৈরি করেন তবে এটি জলের বাম দিকে তরঙ্গ তৈরি করবে, যা আমি অবাস্তব বলে মনে করি। এছাড়াও, যেহেতু আমি তরঙ্গগুলি মোড়ানো করি না, তাই পটভূমিগুলি খুব দ্রুত ফ্ল্যাট হয়ে যায়। সুতরাং আমি এগুলিকে কেবল গ্রাফিকাল এফেক্ট হিসাবে বেছে নিয়েছি, যেমন মিকেল হগ্রস্ট্রাম বলেছিলেন, যাতে ব্যাকগ্রাউন্ড ওয়েভগুলি গতি এবং ত্বরণের জন্য গণনার মধ্যে অন্তর্ভুক্ত না হয়।
বেরি

1
শুধু তোমাকে জানাতে চাচ্ছি. আমরা "স্প্ল্যাশ-হোল" কে একটি if-বিবৃতি দিয়ে কাটা সম্পর্কে কথা বলেছি। প্রথমে আমি তা করতে নারাজ। তবে এখন আমি লক্ষ্য করেছি যে এটি আসলে নিখুঁতভাবে কাজ করে, যেহেতু ব্যাকগ্রাউন্ড ওয়েভগুলি পৃষ্ঠটি সমতল হতে বাধা দেয়।
বেরি

4
আমি এই তরঙ্গ কোডটি জাভাস্ক্রিপ্টে রূপান্তর করেছি এবং এটি এখানে jsfiddle এ রেখেছি: jsfiddle.net/phil_mcc/sXmpD/8
ফিল ম্যাককালিক

11

সমাধানের জন্য (গাণিতিকভাবে বলতে গেলে আপনি ডিফারেনশিয়াল সমীকরণগুলির সমাধানের মাধ্যমে সমস্যাটি সমাধান করতে পারেন, তবে আমি নিশ্চিত যে তারা ততটা করে না) আপনার কাছে 3 টি সম্ভাবনা রয়েছে (এটি কতটা বিস্তারিতভাবে হওয়া উচিত তার উপর নির্ভর করে):

  1. ত্রিকোণমিতিক ফাংশন (সবচেয়ে সহজ এবং দ্রুত) সাথে তরঙ্গ গণনা করুন
  2. আনকো যেমন প্রস্তাব করেছে তেমন করো
  3. ডিফারেনশিয়াল সমীকরণ সমাধান করুন
  4. টেক্সচার লুকআপ ব্যবহার করুন

সমাধান 1

সত্যই সহজ, প্রতিটি তরঙ্গের জন্য আমরা পৃষ্ঠার প্রতিটি বিন্দু থেকে উত্সের (পরম) দূরত্ব গণনা করি এবং সূত্রের সাহায্যে আমরা 'হাইট' গণনা করি

1.0f/(dist*dist) * sin(dist*FactorA + Phase)

কোথায়

  • দূর আমাদের দূরত্ব
  • কারখানা একটি মান যার অর্থ তরঙ্গগুলি কত গতিযুক্ত / ঘন হওয়া উচিত
  • পর্যায়টি তরঙ্গের পর্যায়, একটি অ্যানিমেটেড তরঙ্গ পেতে সময় সহ আমাদের এটি বাড়ানো দরকার

নোট করুন যে আমরা আমাদের পছন্দ মতো আরও পদ যুক্ত করতে পারি (সুপারপজিশন নীতি))

স্বপক্ষে

  • এটি গণনা করা খুব দ্রুত
  • কার্যকর করা সহজ

বিরূদ্ধে

  • 1 তল পৃষ্ঠের (সরল) প্রতিচ্ছবিগুলির জন্য প্রতিচ্ছবি অনুকরণের জন্য আমাদের "ভূত" তরঙ্গ উত্স তৈরি করা দরকার, এটি 2 ডি পৃষ্ঠায় আরও জটিল এবং এটি এই সাধারণ পদ্ধতির সীমাবদ্ধতার মধ্যে একটি is

সমাধান 2

স্বপক্ষে

  • এটি খুব সহজ
  • এটি সহজেই প্রতিচ্ছবি গণনা করতে দেয়
  • এটি সহজেই সহজেই 3 ডি বা 3 ডি স্পেসে বাড়ানো যেতে পারে

বিরূদ্ধে

  • ডাম্পিংয়ের মান খুব বেশি হলে সংখ্যাগতভাবে অস্থির হয়ে উঠতে পারে
  • সমাধান 1 এর চেয়ে বেশি গণনার শক্তি প্রয়োজন (তবে সলিউশন 3 এর মতো তেমন নয় )

সমাধান 3

এখন আমি একটি শক্ত প্রাচীর আঘাত, এটি সবচেয়ে জটিল সমাধান।

আমি এটিকে বাস্তবায়ন করি নি তবে এই দানবগুলি সমাধান করা সম্ভব।

এখানে আপনি এর গণিত সম্পর্কে একটি উপস্থাপনা পেতে পারেন, এটি সহজ নয় এবং বিভিন্ন ধরণের তরঙ্গের জন্য ডিফারেনশিয়াল সমীকরণও রয়েছে।

আরও বিশেষ কেসগুলি সমাধানের জন্য কিছু ডিফারেনশিয়াল সমীকরণ সহ একটি সম্পূর্ণ তালিকা এখানে নেই (সলিটনস, পিকনস, ...)

স্বপক্ষে

  • বাস্তব তরঙ্গ

বিরূদ্ধে

  • বেশিরভাগ গেমের জন্য প্রচেষ্টা মূল্য নয়
  • সর্বাধিক গণনার সময় প্রয়োজন

সমাধান 4

সমাধান 1 এর চেয়ে কিছুটা জটিল তবে এত জটিল জটিল নয় 3।

আমরা প্রাক্কলকুলেটেড টেক্সচার ব্যবহার করি এবং সেগুলিকে একসাথে মিশ্রিত করি, এর পরে আমরা স্থানচ্যুতি ম্যাপিং ব্যবহার করি (আসলে 2 ডি ওয়েভের জন্য একটি পদ্ধতি তবে নীতিটি 1 ডি ওয়েভের জন্যও কাজ করতে পারে)

গেম স্টর্মোভিক এই পদ্ধতির ব্যবহার করেছে তবে আমি এটি সম্পর্কিত নিবন্ধটির লিঙ্কটি পাই না।

স্বপক্ষে

  • এটি 3 এর চেয়ে বেশি সাধারণ
  • এটি দেখতে ভাল ফলাফল পায় (2 ডি এর জন্য)
  • শিল্পীরা যদি দুর্দান্ত কাজ করে তবে এটি বাস্তববাদী দেখতে পারে

বিরূদ্ধে

  • প্রাণবন্ত করা কঠিন
  • বারবার নিদর্শন দিগন্তে দৃশ্যমান হতে পারে

6

অবিচ্ছিন্ন তরঙ্গ যুক্ত করতে আপনি গতিশীলতার গণনা করার পরে কয়েকটা সাইন-ওয়েভ যুক্ত করুন। সরলতার জন্য আমি এই স্থানচ্যুতিটি কেবল একটি গ্রাফিকাল এফেক্ট হিসাবে তৈরি করব এবং এটি নিজেই গতিবিদ্যাকে প্রভাবিত করতে দেবে না তবে আপনি উভয় বিকল্পের চেষ্টা করতে পারেন এবং দেখতে পারেন কোনটি সবচেয়ে ভাল কাজ করে।

"স্প্ল্যাশহোল" আরও ছোট করার জন্য আমি স্প্ল্যাশ পদ্ধতিটি পরিবর্তনের পরামর্শ দেব (ইনট সূচক, ভাসমান গতি) যাতে এটি কেবল সূচককেই প্রভাবিত করে না তবে কিছু ঘনিষ্ঠ কোণকেও প্রভাবিত করে, যাতে প্রভাবটি ছড়িয়ে দেয় তবে এখনও একই থাকে " শক্তি". আপনার অবজেক্টটি কত প্রশস্ত তার উপর নির্ভর করে উল্লম্বের সংখ্যা নির্ভর করতে পারে। আপনার নিখুঁত ফলাফল পাওয়ার আগে আপনাকে সম্ভবত প্রভাবটি অনেকটা টুইট করতে হবে।

জলের গভীর অংশগুলি টেক্সচার করার জন্য আপনি হয় নিবন্ধে বর্ণিত হিসাবে করতে পারেন এবং কেবল গভীর অংশটিকে "আরও নীল" করতে পারেন বা আপনি পানির গভীরতার উপর নির্ভর করে দুটি টেক্সচারের মধ্যে বিভক্ত করতে পারেন।


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

1
ঠিক আছে, তবে যদি আপনার নির্দিষ্ট কিছু সাহায্যের দরকার হয় তবে কেবল তাই বলুন এবং আমি আরও কিছুটা বিস্তৃত হতে পারি কিনা তা আমি দেখতে পাচ্ছি।
মিকেল হগ্রাস্টম

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