Вращение прямоугольника над волнами

avatar
Juan Álvarez
27 октября 2021 в 16:53
98
1
3

Я пытаюсь перемещать прямоугольник по волнам, пытаясь имитировать движение лодки. Для этого я поворачиваю прямоугольник, используя высоту нарисованных линий и вычисляя угол, который они образуют с прямоугольником. Однако почему-то в некоторых точках волн прямоугольник мерцает. Код показывает проблему.

import sys
import math
import pygame
from pygame.locals import *

pygame.init()
SCREEN_WIDTH = 900
SCREEN_HEIGHT = 900
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))

clock = pygame.time.Clock()

BLUE = (0, 103, 247)
RED = (255,0,0)

watter_levels = [0 for _ in range(SCREEN_WIDTH)]

def draw_water(surface, dy):
    amplitude = 35
    global watter_levels
    for x in range(SCREEN_WIDTH):
        y = int(math.sin((x)*(0.01)) * amplitude + dy)
        watter_levels[x] = y
        pygame.draw.aaline(surface,BLUE,(x,y),(x,y))

def get_water_level(index):
    if index <= 0:
        return watter_levels[0]
    if index >= SCREEN_WIDTH:
        return watter_levels[-1]
    return watter_levels[index]

font = pygame.font.Font(None,30)
def debug(info,x=10,y=10):
    display_surf = pygame.display.get_surface()
    debug_surface = font.render(str(info),True,'White')
    debug_rect = debug_surface.get_rect(topleft=(x,y))
    pygame.draw.rect(display_surf,'Black',debug_rect)
    display_surf.blit(debug_surface,debug_rect)

class Ship(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image = pygame.Surface((80,80),pygame.SRCALPHA)
        self.image.fill('red')
        self.rect = self.image.get_rect(topleft=(0,0))
        self.copy_img = self.image.copy()
        self.move = pygame.math.Vector2(0,0)
        self.copy_img = self.image.copy()
        self.velocity = 8

    def rotate(self, angle):
        self.image = pygame.transform.rotate(self.copy_img, int(angle))
        self.rect = self.image.get_rect(center=(self.rect.center))

    def update(self):
        self.get_input()
        self.rect.bottom = get_water_level(self.rect.centerx)
        left_y = get_water_level(self.rect.left)
        right_y = get_water_level(self.rect.right)
        angle = 180*math.atan2(left_y-right_y,self.image.get_width())/math.pi
        debug("angle: "+str(int(angle)))
        print(self.rect.left,self.rect.right)
        self.rotate(angle)

    def replicate(self):
        if self.rect.left == 363:
            return
        self.rect.x += 1

    def get_input(self):
        self.replicate()
        # keys = pygame.key.get_pressed()
        # if keys[K_LEFT]:
        #     self.move.x = -self.velocity
        # elif keys[K_RIGHT]:
        #     self.move.x = self.velocity
        # else:
        #     self.move.x = 0
        # if keys[K_UP]:
        #     self.move.y = -self.velocity
        # elif keys[K_DOWN]:
        #     self.move.y = self.velocity
        # else:
        #     self.move.y = 0
        # self.rect.x += self.move.x
        # self.rect.y += self.move.y
        # if self.rect.left <= 0:
        #     self.rect.left = 0
        # if self.rect.right >= SCREEN_WIDTH:
        #     self.rect.right = SCREEN_WIDTH


ship_sprite = pygame.sprite.GroupSingle()
ship_sprite.add(Ship())
while True:
    screen.fill((200,210,255,0))
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()

    ship_sprite.update()
    ship_sprite.draw(screen) 
    draw_water(screen,SCREEN_HEIGHT-300)   
    clock.tick(60)
    pygame.display.update()
Источник

Ответы (1)

avatar
Rabbid76
27 октября 2021 в 18:58
3

Все дело в точности. Вы можете улучшить результат, сохранив уровень воды с точностью с плавающей запятой:

def draw_water(surface, dy):
    amplitude = 35
    global watter_levels
    for x in range(SCREEN_WIDTH):
        y = math.sin(x * 0.01) * amplitude + dy
        watter_levels[x] = y
        pygame.draw.aaline(surface, BLUE, (x, round(y)), (x, round(y)))

Кроме того, вы должны получить левый и правый из "неповернутого" прямоугольника:

class Ship(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image = pygame.Surface((80,80),pygame.SRCALPHA)
        self.image.fill('red')
        self.rect = self.image.get_rect(topleft = (0, 0))
        self.copy_img = self.image.copy()
        self.copy_rect = pygame.Rect(self.rect)
        self.move = pygame.math.Vector2(0, 0)
        self.velocity = 8
        
    def rotate(self, angle):
        self.image = pygame.transform.rotate(self.copy_img, round(angle))
        self.rect = self.image.get_rect(center = (self.copy_rect.center))

    def update(self):
        self.get_input()
        self.copy_rect.bottom = round(get_water_level(self.copy_rect.centerx))
        left_y = get_water_level(self.copy_rect.left)
        right_y = get_water_level(self.copy_rect.right)
        angle = math.degrees(math.atan2(left_y-right_y, self.copy_img.get_width()))
        debug("angle: " + str(round(angle)))
        print(self.copy_rect.left, self.copy_rect.right)
        self.rotate(angle)

    def replicate(self):
        if self.copy_rect.left == 363:
            return
        self.copy_rect.x += 1