首页 > 解决方案 > Pygame 水物理没有按预期工作

问题描述

所以,我一直在尝试根据本教程在 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)

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

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

标签: python-3.xpygamephysicspygame-surface

解决方案


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

稍微修改了数字,并得到了一些真正漂亮的飞溅。(比公认的解决方案更好!:( 哈哈。

如果您需要更多地解决这个问题,我建议您将扩展件注释掉(或将其赋值为零),您可以自行检查水的弹性。这使得 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)

推荐阅读