Pygame 水物理无法按预期工作

Eve*_*lyn 5 pygame physics python-3.x pygame-surface

所以,我一直在尝试根据本教程在 pygame 中实现水物理

https://gamedevelopment.tutsplus.com/tutorials/make-a-splash-with-dynamic-2d-water-effects--gamedev-236

问题是,当我在 pygame 中实现这段代码时,水决定它不会产生很好的涟漪效应,而是会发疯并在整个屏幕上摆动,直到最终崩溃。

我环顾了一些不和谐的服务器,发现其他人试图实现同样的事情,并遇到了同样的问题。他们的代码基本上和我的一样,只是组织得更整齐,所以我会发布它而不是我的。

import pygame, random
import math as m

from pygame import *

pygame.init()

WINDOW_SIZE = (854, 480)
 
screen = pygame.display.set_mode(WINDOW_SIZE,0,32) # initiate the window

clock = pygame.time.Clock()

font = pygame.font.SysFont("Arial", 18)

class surface_water_particle():
    def __init__(self, x,y):
        self.x_pos = x
        self.y_pos = y
        self.target_y = y
        self.velocity = 0
        self.k = 0.02
        self.d = 0.02
        self.time = 1

    def update(self):
        x = -(self.target_y - self.y_pos)
        a = -(self.k * x - self.d * self.velocity)

        self.y_pos += self.velocity
        self.velocity += a

class water():
    def __init__(self, x_start, x_end, y_start, y_end, segment_length):
        self.springs = []
        self.x_start = x_start
        self.y_start = y_start
        self.x_end = x_end
        self.y_end = y_end - 10
        for i in range(abs(x_end - x_start) // segment_length):
            self.springs.append(surface_water_particle(i * segment_length + x_start, y_end))

    def update(self, spread):
        for i in range(len(self.springs)):
            self.springs[i].update()

        leftDeltas = [0] * len(self.springs)
        rightDeltas = [0] * len(self.springs)

        for i in range(0, len(self.springs) ):
            if i > 0:
                leftDeltas[i] = spread * (self.springs[i].y_pos - self.springs[i - 1].y_pos)
                self.springs[i - 1].velocity += leftDeltas[i]
            if i < len(self.springs) - 1:
                rightDeltas[i] = spread * (self.springs[i].y_pos - self.springs[i + 1].y_pos)
                self.springs[i + 1].velocity += rightDeltas[i]

        for i in range(0, len(self.springs) ):
            if i > 0:
                self.springs[i - 1].velocity += leftDeltas[i]
            if i < len(self.springs) - 1:
                self.springs[i + 1].velocity += rightDeltas[i]
                
                
    def splash(self, index, speed):
        if index > 0 and index < len(self.springs) :
            self.springs[index].velocity = speed
                
    def draw(self):
        water_surface = pygame.Surface((abs(self.x_start - self.x_end), abs(self.y_start - self.y_end))).convert_alpha()
        water_surface.fill((0,0,0,0))
        water_surface.set_colorkey((0,0,0,0))
        polygon_points = []
        polygon_points.append((self.x_start, self.y_start))
        for spring in range(len(self.springs)):
            polygon_points.append((water_test.springs[spring].x_pos, water_test.springs[spring].y_pos))
        polygon_points.append((water_test.springs[len(self.springs) - 1].x_pos, self.y_start))

        #pygame.draw.polygon(water_surface, (0,0,255), polygon_points)
        
        for spring in range(0,len(self.springs) - 1):
            pygame.draw.line(screen, (0,0,255), (water_test.springs[spring].x_pos, water_test.springs[spring].y_pos), (water_test.springs[spring + 1].x_pos, water_test.springs[spring + 1].y_pos), 2)

        #water_surface.set_alpha(100)

        return water_surface

def update_fps():
    fps_text = font.render(str(int(clock.get_fps())), 1, pygame.Color("coral"))
    screen.blit(fps_text, (0,0))

water_test = water(0,800,200,80, 20)

while True:
    screen.fill((255,255,255))
    water_test.update(0.5)
    screen.blit(water_test.draw(), (0,0))
    
    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
        if event.type == MOUSEBUTTONDOWN:
            water_test.splash(10,0.1)

    pygame.display.update()

    clock.tick(60)
Run Code Online (Sandbox Code Playgroud)

我试图寻找我们的教程实现的任何问题,但我找不到任何似乎不合适的地方。然后我尝试添加一个阻力函数,该函数可以在每次更新时划分水流速度。然而这并没有解决问题。

有谁知道做错了什么以及如何解决这个问题?

Air*_*uid 3

许多与其他帖子相同的观察结果。我评论了一些事情,稍微改变了你的速度陈述。在“传播”中我添加了通行证。

对数字进行了一些修改,并得到了一些非常漂亮的效果。(比公认的解决方案更好!:( 哈哈。

如果您需要进一步解决此问题,我建议您注释掉扩展件(或将其值设置为零),然后您可以自行检查水的弹性。这使得 T/S 变得更加容易。

import pygame, random
import math as m

from pygame import *

pygame.init()

WINDOW_SIZE = (854, 480)
 
screen = pygame.display.set_mode(WINDOW_SIZE,0,32) # initiate the window

clock = pygame.time.Clock()

font = pygame.font.SysFont("Arial", 18)

class surface_water_particle():
    k = 0.04  # spring constant
    d = 0.08  # damping constant
    def __init__(self, x,y):
        self.x_pos = x
        self.y_pos = y
        self.target_y = y
        self.velocity = 0

    def update(self):
        x = self.y_pos - self.target_y              # displacement of "spring"
        a = -self.k * x - self.d * self.velocity    # unit of acceleration

        self.y_pos += self.velocity
        self.velocity += a

class water():
    
    def __init__(self, x_start, x_end, y_start, y_end, segment_length):
        self.springs = []
        self.x_start = x_start
        self.y_start = y_start
        self.x_end = x_end
        self.y_end = y_end - 10
        for i in range(abs(x_end - x_start) // segment_length):
            self.springs.append(surface_water_particle(i * segment_length + x_start, y_end))

    def update(self, spread):
        passes = 4  # more passes = more splash spreading
        for i in range(len(self.springs)):
            self.springs[i].update() 

        leftDeltas = [0] * len(self.springs)
        rightDeltas = [0] * len(self.springs)
        for p in range(passes):  
            for i in range(0, len(self.springs) -1 ):
                if i > 0:  
                    leftDeltas[i] = spread * (self.springs[i].y_pos - self.springs[i - 1].y_pos)
                    self.springs[i - 1].velocity += leftDeltas[i]
                if i < len(self.springs) - 1:
                    rightDeltas[i] = spread * (self.springs[i].y_pos - self.springs[i + 1].y_pos)
                    self.springs[i + 1].velocity += rightDeltas[i]

            for i in range(0, len(self.springs) -1):
                if i > 0:
                    self.springs[i - 1].y_pos += leftDeltas[i]  # you were updating velocity here!
                if i < len(self.springs) - 1:
                    self.springs[i + 1].y_pos += rightDeltas[i]

               
                
    def splash(self, index, speed):
        if index > 0 and index < len(self.springs) :
            self.springs[index].velocity = speed
                
    def draw(self):
        water_surface = pygame.Surface((abs(self.x_start - self.x_end), abs(self.y_start - self.y_end))).convert_alpha()
        water_surface.fill((0,0,0,0))
        water_surface.set_colorkey((0,0,0,0))
        polygon_points = []
        polygon_points.append((self.x_start, self.y_start))
        for spring in range(len(self.springs)):
            polygon_points.append((water_test.springs[spring].x_pos, water_test.springs[spring].y_pos))
        polygon_points.append((water_test.springs[len(self.springs) - 1].x_pos, self.y_start))

        #pygame.draw.polygon(water_surface, (0,0,255), polygon_points)
        
        for spring in range(0,len(self.springs) - 1):
            pygame.draw.line(screen, (0,0,255), (water_test.springs[spring].x_pos, water_test.springs[spring].y_pos), (water_test.springs[spring + 1].x_pos, water_test.springs[spring + 1].y_pos), 2)

        #water_surface.set_alpha(100)

        return water_surface

def update_fps():
    fps_text = font.render(str(int(clock.get_fps())), 1, pygame.Color("coral"))
    screen.blit(fps_text, (0,0))

water_test = water(0,800,200,200, 3)

while True:
    screen.fill((255,255,255))
    water_test.update(0.025)
    screen.blit(water_test.draw(), (0,0))
    
    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
        if event.type == MOUSEBUTTONDOWN:
            water_test.splash(50,100)

    pygame.display.update()

    clock.tick(60)
Run Code Online (Sandbox Code Playgroud)