मैं गतिशील तरंगों के साथ 2 डी पानी कैसे बनाऊं?


81

नई सुपर मारियो ब्रोस में वास्तव में 2 डी पानी है जो मैं बनाना चाहता हूं।

यहां एक वीडियो दिखा रहा है। एक उदाहरण हिस्सा:

नई सुपर मारियो पानी के प्रभाव

पानी से टकराने वाली चीजें लहरें पैदा करती हैं। निरंतर "पृष्ठभूमि" लहरें भी हैं। आप वीडियो में 00:50 बजे के बाद निरंतर तरंगों पर एक अच्छा रूप देख सकते हैं, जब कैमरा हिल नहीं रहा है।

मैं मानती हूं कि स्प्लैश इफेक्ट्स इस ट्यूटोरियल के पहले भाग की तरह काम करते हैं ।

हालांकि, NSMB में पानी की सतह पर निरंतर तरंगें होती हैं, और स्पलैश बहुत अलग दिखते हैं। एक और अंतर यह है कि ट्यूटोरियल में, यदि आप छप बनाते हैं, तो यह पहले छप के मूल में पानी में एक गहरा "छेद" बनाता है। नए सुपर मारियो ब्रोस में यह छेद अनुपस्थित या बहुत छोटा है। मैं उन छींटों का जिक्र कर रहा हूं जो खिलाड़ी पानी में और बाहर कूदते समय बनाता है।

मैं निरंतर तरंगों और स्प्लैश के साथ पानी की सतह कैसे बना सकता हूं?

मैं XNA में प्रोग्रामिंग कर रहा हूं। मैंने स्वयं यह कोशिश की है, लेकिन मैं वास्तव में गतिशील तरंगों के साथ अच्छी तरह से काम करने के लिए पृष्ठभूमि साइन तरंगों को प्राप्त नहीं कर सका ।

मैं यह नहीं पूछ रहा हूं कि न्यू सुपर मारियो ब्रोज़ के डेवलपर्स ने ऐसा कैसे किया - बस इस तरह से एक प्रभाव को फिर से बनाने के लिए कैसे इसमें रुचि रखते हैं।

जवाबों:


147

मैं इसे करने की कोशिश की।

स्पलैश (स्प्रिंग्स)

जैसा कि उस ट्यूटोरियल में उल्लेख किया गया है, पानी की सतह एक तार की तरह है: यदि आप तार के किसी बिंदु पर खींचते हैं, तो उस बिंदु के आगे के बिंदु भी नीचे खींच लिए जाएंगे। सभी बिंदुओं को एक आधार रेखा पर वापस आकर्षित किया जाता है।

यह मूल रूप से एक दूसरे के बगल में लंबवत स्प्रिंग्स के बहुत सारे हैं जो एक दूसरे पर भी खींचते हैं।

मैंने ल्युवे का उपयोग करके लुआ में स्केच किया और यह पाया:

एक दिखावा का एनीमेशन

प्रशंसनीय लगता है। ओह हूक , तुम सुंदर प्रतिभा।

यदि आप इसके साथ खेलना चाहते हैं, तो यहां एक जावास्क्रिप्ट पोर्ट शिष्टाचार फिल का है ! मेरा कोड इस उत्तर के अंत में है।

पृष्ठभूमि की लहरें (खड़ी हुई सेंस)

प्राकृतिक पृष्ठभूमि तरंगें मुझे साइन लहरों के एक समूह (विभिन्न आयामों, चरणों और तरंग दैर्ध्य के साथ) की तरह दिखती हैं। जब मैंने इसे लिखा था तो यहां ऐसा था:

साइन हस्तक्षेप द्वारा निर्मित पृष्ठभूमि तरंगें

हस्तक्षेप पैटर्न बहुत प्रशंसनीय दिखते हैं।

अब सब एक साथ

तो फिर छप तरंगों और पृष्ठभूमि तरंगों को एक साथ जोड़ना एक बहुत ही सरल बात है:

स्प्लिट्स के साथ बैकग्राउंड वेव्स

जब स्पलैश होता है, तो आप छोटे ग्रे सर्कल दिखाते हुए देख सकते हैं कि मूल पृष्ठभूमि तरंग कहां होगी।

यह आपके द्वारा लिंक किए गए वीडियो जैसा दिखता है , इसलिए मैं इसे एक सफल प्रयोग मानूंगा।

यहाँ मेरी 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

बहुत बढ़िया जवाब! आपका बहुत बहुत धन्यवाद। और यह भी, मेरे प्रश्न को संशोधित करने के लिए धन्यवाद, मैं देख सकता हूं कि यह कैसे अधिक स्पष्ट है। साथ ही gif बहुत मददगार होते हैं। क्या आप संयोग से उस बड़े छेद को रोकने का एक तरीका जानते हैं जो छप बनाते समय उभरता है? यह हो सकता है कि मिकेल होगस्ट्रॉम ने पहले ही इस अधिकार का जवाब दिया था, लेकिन मैंने कोशिश की थी कि इस प्रश्न को पोस्ट करने से पहले भी और मेरा नतीजा यह था कि छेद त्रिकोणीय आकार का हो गया और यह बहुत अवास्तविक लग रहा था।
बेरी

"स्प्लैश होल" की गहराई को कम करने के लिए, आप तरंग के अधिकतम आयाम को कैप कर सकते हैं अर्थात बेसलाइन से किसी भी बिंदु को भटकने की अनुमति कितनी दूर है।
अंको

3
रुचि रखने वाले किसी व्यक्ति के लिए बीटीडब्ल्यू: पानी के किनारों को लपेटने के बजाय, मैंने पक्षों को सामान्य करने के लिए आधार रेखा का उपयोग करने का विकल्प चुना। अन्यथा, यदि आप पानी के दाईं ओर छप बनाते हैं, तो यह पानी के बाईं ओर भी तरंगें पैदा करेगा, जिसे मैंने अवास्तविक पाया। इसके अलावा, चूंकि मैंने तरंगों को लपेटा नहीं था, इसलिए बैकग्राउंड बहुत जल्दी फ्लैट हो जाएंगे। इसलिए मैंने उन लोगों को केवल एक ग्राफिकल प्रभाव बनाने के लिए चुना, जैसे कि मिकेल होर्गस्ट्रॉम ने कहा, ताकि पृष्ठभूमिवालों को गति और त्वरण के लिए गणना में शामिल नहीं किया जाएगा।
बेरी

1
बस आपको बताना चाहता था। हमने इफ-स्टेटमेंट के साथ "स्प्लैश-होल" को ट्रंक करने के बारे में बात की है। पहले तो मैं ऐसा करने से हिचक रहा था। लेकिन अब मैंने देखा है कि यह वास्तव में पूरी तरह से काम करता है, क्योंकि बैकग्राउंड सतह को सपाट होने से रोकेगा।
बेरी

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 डी सतहों पर अधिक जटिल है और यह इस सरल दृष्टिकोण की सीमाओं में से एक है

समाधान २

समर्थक

  • इसका सरल भी
  • यह आसानी से प्रतिबिंबों की गणना करने की अनुमति देता है
  • इसे 2d या 3D स्पेस में आसानी से रिलेटिवली बढ़ाया जा सकता है

कॉन्ट्रा

  • डंपिंग मूल्य बहुत अधिक होने पर संख्यात्मक रूप से अस्थिर हो सकता है
  • समाधान 1 से अधिक गणना शक्ति की आवश्यकता है (लेकिन समाधान 3 की तरह नहीं )

समाधान 3

अब मैंने एक कठिन दीवार को मारा, यह सबसे जटिल समाधान है।

मैंने इसे लागू नहीं किया, लेकिन इन राक्षसों को हल करना संभव है।

यहां आप इसके गणित के बारे में एक प्रस्तुति पा सकते हैं, इसकी सरल नहीं है और विभिन्न प्रकार की तरंगों के लिए अंतर समीकरण भी मौजूद हैं।

यहाँ कुछ विशेष मामलों को हल करने के लिए कुछ अंतर समीकरणों के साथ एक पूरी सूची नहीं है (सोलिटन्स, पीकन्स, ...)

समर्थक

  • यथार्थवादी तरंगें

कॉन्ट्रा

  • अधिकांश खेलों के प्रयास के लायक नहीं
  • सबसे अधिक गणना समय की आवश्यकता है

समाधान 4

समाधान 1 की तुलना में थोड़ा अधिक जटिल है लेकिन समाधान 3 इतना जटिल नहीं है।

हम पूर्वगामी बनावट का उपयोग करते हैं और उन्हें एक साथ मिलाते हैं, उसके बाद हम विस्थापन मानचित्रण का उपयोग करते हैं (वास्तव में 2d तरंगों के लिए एक विधि लेकिन सिद्धांत 1d तरंगों के लिए भी काम कर सकते हैं)

खेल स्टॉर्मोविक ने इस दृष्टिकोण का उपयोग किया है, लेकिन मुझे इसके बारे में लेख का लिंक नहीं मिला है।

समर्थक

  • यह 3 से अधिक सरल है
  • इसके अच्छे परिणाम देखने को मिलते हैं (2d के लिए)
  • यदि कलाकार अच्छा काम करते हैं तो यह यथार्थवादी लग सकता है

कॉन्ट्रा

  • मुश्किल से चेतन
  • दोहराया पैटर्न क्षितिज पर दिखाई दे सकता है

6

निरंतर तरंगों को जोड़ने के लिए आप डायनामिक्स की गणना करने के बाद साइन-लहरों के एक जोड़े को जोड़ते हैं। सादगी के लिए मैं इस विस्थापन को केवल एक ग्राफिकल प्रभाव बनाऊंगा और इसे स्वयं गतिशीलता को प्रभावित नहीं करने दूंगा लेकिन आप दोनों विकल्पों को आजमा सकते हैं और देख सकते हैं कि कौन सा सबसे अच्छा काम करता है।

"स्पलैशहोल" को छोटा करने के लिए मैं स्प्लैश (इंट इंडेक्स, फ्लोट स्पीड) विधि को बदलने का सुझाव दूंगा ताकि यह सीधे न केवल इंडेक्स को प्रभावित करे बल्कि कुछ करीबी वर्टिकल को भी प्रभावित करे, ताकि प्रभाव को फैलाने के लिए लेकिन अभी भी वही हो " ऊर्जा"। प्रभावित स्थानों की संख्या इस बात पर निर्भर कर सकती है कि आपकी वस्तु कितनी चौड़ी है। इससे पहले कि आप एक पूर्ण परिणाम प्राप्त करें, आपको संभवतः प्रभाव को बहुत छोटा करना होगा।

पानी के गहरे हिस्सों को बनावट के लिए आप या तो लेख में वर्णित कर सकते हैं और गहरे हिस्से को "अधिक नीला" बना सकते हैं या आप पानी की गहराई के आधार पर दो बनावटों के बीच अंतर कर सकते हैं।


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

1
ठीक है, लेकिन अगर कुछ विशिष्ट है, तो आपको मदद की ज़रूरत है, बस इतना कहो और मैं देखूंगा कि क्या मैं थोड़ा अधिक विस्तृत हो सकता हूं।
मिकेल होजस्ट्रम 18

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