Pygame — Python проблема с математикой

Вот код, по идее он должен рисовать впереди едущей машинки небольшие 4х4 прямоугольники (в итоги они накладываются друг на друга и выглядят как линия). При это спрайт машины может быть направлен под любым углом. И проблема заключается в том, что эта линия из прямоугольников не во всех случаях идет под тем же углом что и машинка. При вводе целых углов (0, 90, 180...), линия отображается правильно (см. рисунок, 3 картинка). А при углах например 222 градуса или 333, угол наклона линии и траектории движения машинки не совпадают. Я думая, что проблема в расчетах координат прямоугольников, только не пойму где ошибка.

d5e9b1180494f1cd5f0b09b3e948de9a.png
Код для Python:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from __future__ import division # / - деление
import pygame, math
from pygame import *
WIN_WIDTH = 800 #Ширина создаваемого окна
WIN_HEIGHT = 600 # Высота
SKREEN_COLOR = (100,255,200)
WIDTH = 3000  #Ширина поля робота
HEIGHT = 3000 #Высота
PATH_ROBOT_IMAGE = 'image/robot.png'
######## ПЛАТФОРМЫ ###########
PLATFORM_WIDTH = 4
PLATFORM_HEIGHT = 4
PLATFORM_COLOR = "#FF6262"
##############################
backward = forward = False #Робот стоит
############## Класс для отрисовки робота
class Sprite(pygame.sprite.Sprite): #Наследование класса Sprite
    def __init__(self, filename, startx, starty):
	pygame.sprite.Sprite.__init__(self)#инициализация __init__ в классе Sprite
        self.image = pygame.image.load(filename)
        self.rect = self.image.get_rect()
	self.rect.x = startx
	self.rect.y = starty
    def render(self, screen, pos = (0, 0), angle = 0):
        #Поворачиваю картинку
        image = pygame.transform.rotate(self.image, angle)
        self.rect = image.get_rect(center=self.rect.center)
        screen.blit(image, self.rect)
    def move(self, forward, backward, angle, path):
   	rad_alfa = angle * (math.pi/180)
	if forward: 
           self.rect.x = self.rect.x + path*math.cos(rad_alfa)
	   self.rect.y = self.rect.y - path*math.sin(rad_alfa)
	   
	if backward:
	   self.rect.y +=path*math.sin(rad_alfa)
	   self.rect.x -=path*math.cos(rad_alfa)
    def get_position(self):
	return self.rect.center
    #image = 0

############## Класс для отрисовки платформ
class Platform(pygame.sprite.Sprite):
    def __init__(self, x, y):
	pygame.sprite.Sprite.__init__(self)#инициализация __init__ в классе Sprite
        self.image = Surface((PLATFORM_WIDTH, PLATFORM_HEIGHT))
        self.image.fill(Color(PLATFORM_COLOR))
        self.rect = Rect(x, y, PLATFORM_WIDTH, PLATFORM_HEIGHT)

pygame.init()
screen = pygame.display.set_mode((WIN_WIDTH, WIN_HEIGHT)) #Размер экрана
robot = Sprite(PATH_ROBOT_IMAGE, 400, 300) #Создание робота на координатах 0,0

entities = pygame.sprite.Group() # Все объекты
platforms = [] # то, во что мы будем врезаться или опираться
#entities.add(robot)#Добавляем спрайт робота в группу ко всем объектам


def position(x0 ,y0, alfap, distanse):
    rad_alfap = alfap * (math.pi/180)
    y = float(y0) + (math.sin(rad_alfap)*distanse)
    x = float(x0) + (math.cos(rad_alfap)*distanse)
    pos=(x, y) #Отображаем ось Oy вертикально вверх
    print pos
    return pos

clok = pygame.time.Clock()
while True:
    for e in pygame.event.get():
        if e.type == pygame.QUIT: exit(0)
        if e.type == pygame.KEYDOWN and e.key == pygame.K_ESCAPE: exit(0)
    
    screen.fill((100, 255, 100))

    forward = True #Движение вперед
    angle =222 #Угол движения (222 90 333)
    path =2 #Cкорость движения
    robot.move(forward, backward, angle, path) #Движение робота
    distanse = 70 # Расстояния на котором отмечается прямоугольник перед роботом
    pos_robot = robot.get_position() #Получения координат спрайта робота (размер 25х16)
    ppos = position(pos_robot[0], -(pos_robot[1]), angle, distanse) #Нахождении координат прямоугольника
    robot.render(screen, (400, 400), angle) #Поворот спрайта на угол angle
    pf = Platform(ppos[0], -ppos[1]) 
    entities.add(pf)
    platforms.append(pf)

    entities.draw(screen)
    pygame.display.update()

    clok.tick(30)

  • Вопрос задан
  • 5508 просмотров
Пригласить эксперта
Ответы на вопрос 3
@Nicknnn
Что-то не так с самой формулой расчёта движения машинки. Если поставить угол 45 и скорость например 10, то траектория уже станет гораздо ближе к истине. А со скоростью 1 машинка бдет двигаться строго вертикально независимо от угла.
Ответ написан
@Nicknnn
Хотя вот вариант. Сохранять координату отдельно.
Тока нужно доработать ещё get_position. Пусть лучше возвращяет сохранённые координаты.

class Sprite(pygame.sprite.Sprite): #Наследование класса Sprite
    def __init__(self, filename, startx, starty):
        pygame.sprite.Sprite.__init__(self)#инициализация __init__ в классе Sprite
        self.image = pygame.image.load(filename)
        self.rect = self.image.get_rect()
        self.rect.x = startx
        self.rect.y = starty
        self.x = float(startx)
        self.y = float(startx)
    def render(self, screen, pos = (0, 0), angle = 0):
        #Поворачиваю картинку
        image = pygame.transform.rotate(self.image, angle)
        self.rect = image.get_rect(center=self.rect.center)
        screen.blit(image, self.rect)
    def move(self, forward, backward, angle, path):
        rad_alfa = angle * (math.pi/180)
        if forward:
            self.x = self.x + (path * math.cos(rad_alfa))
            self.y = self.y - (path * math.sin(rad_alfa))
            self.rect.x = self.x
            self.rect.y = self.y
        if backward:
            self.y +=path*math.sin(rad_alfa)
            self.x -=path*math.cos(rad_alfa)
            self.rect.x = self.x
            self.rect.y = self.y
    def get_position(self):
        return self.rect.center
Ответ написан
@SKAPeR Автор вопроса
Возникла еще одна небольшая проблема, как видно из функции def render(), повернутая машинка, отрисовывается на экран через screen.blit(image, self.rect) . Мне нужно чтобы камера зафиксировалась относительно машинки и двигалась вместе с ней, все платформы я поместил в один массив entities, и при движении камеры они отрисовываются относительно нее. Используя camera.update(robot), камера фиксируется относительно центра экрана, и получается неправильное отображение всех элементов...
Наверное машинку следует добавить в тот же массив что и платформы, но с повернутой картинкой у меня не получается это сделать
Код для камеры:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from __future__ import division # / - деление
import pygame, math
from pygame import *


WIN_WIDTH = 800 #Ширина создаваемого окна
WIN_HEIGHT = 600 # Высота
SKREEN_COLOR = (100,255,200)
WIDTH = 3000  #Ширина поля
HEIGHT = 3000 #Высота
PATH_ROBOT_IMAGE = 'image/robot.png'
######## ПЛАТФОРМЫ ###########
PLATFORM_WIDTH = 4
PLATFORM_HEIGHT = 4
PLATFORM_COLOR = "#FF6262"
##############################
path = 5

entities = pygame.sprite.Group() # Все объекты
platforms = [] # то, во что мы будем врезаться или опираться

angle = 360 #Начальный уго
backward = forward = False #Робот стоит
############## Класс для отрисовки робота]

class Camera(object):
    def __init__(self, camera_func, width, height):
        self.camera_func = camera_func
        self.state = Rect(0, 0, width, height)
    
    def apply(self, target):
        return target.rect.move(self.state.topleft)

    def update(self, target):
        self.state = self.camera_func(self.state, target.rect)

def camera_configure(camera, target_rect):
    l, t, _, _ = target_rect
    _, _, w, h = camera
    l, t = -l+WIN_WIDTH / 2, -t+WIN_HEIGHT / 2

    l = min(5000, l)                           # Не движемся дальше левой границы
    l = max(-(camera.width-WIN_WIDTH), l)   # Не движемся дальше правой границы
    t = max(-(camera.height-WIN_HEIGHT), t) # Не движемся дальше нижней границы
    t = min(5000, t)                           # Не движемся дальше верхней границы

    return Rect(l, t, w, h)   

camera = Camera(camera_configure, WIDTH, HEIGHT)

class Sprite(pygame.sprite.Sprite): #Наследование класса Sprite
    def __init__(self, filename, startx, starty):
        pygame.sprite.Sprite.__init__(self)#инициализация __init__ в классе Sprite
        self.image = pygame.image.load(filename)
        self.rect = self.image.get_rect()
        self.rect.x = startx
        self.rect.y = starty
        self.x = float(startx)
        self.y = float(startx)
    def render(self, screen, pos = (0, 0), angle = 0):
        #Поворачиваю картинку
	pygame.sprite.Sprite.__init__(self)#инициализация __init__ в классе Sprite
        image = pygame.transform.rotate(self.image, angle)
        self.rect = image.get_rect(center=self.rect.center)
        robot_r = screen.blit(image, self.rect)
    def move(self, forward, backward, angle, path):
        rad_alfa = angle * (math.pi/180)
        if forward:
            self.x = self.x + (path * math.cos(rad_alfa))
            self.y = self.y - (path * math.sin(rad_alfa))
            self.rect.x = self.x
            self.rect.y = self.y
        if backward:
            self.y +=path*math.sin(rad_alfa)
            self.x -=path*math.cos(rad_alfa)
            self.rect.x = self.x
            self.rect.y = self.y
    def get_position(self):
        return self.rect.center

############## Класс для отрисовки платформ
class Platform(pygame.sprite.Sprite):
    def __init__(self, x, y):
	pygame.sprite.Sprite.__init__(self)#инициализация __init__ в классе Sprite
        self.image = Surface((PLATFORM_WIDTH, PLATFORM_HEIGHT))
        self.image.fill(Color(PLATFORM_COLOR))
        self.rect = Rect(x, y, PLATFORM_WIDTH, PLATFORM_HEIGHT)

pygame.init()
screen = pygame.display.set_mode((WIN_WIDTH, WIN_HEIGHT)) #Размер экрана
robot = Sprite(PATH_ROBOT_IMAGE, 0, 0) #Создание робота на координатах 0,0

list_sonic = {1: 0, 2: 45, 3: 90, 4: 180, 5: 270, 6: 315}
def real_alige(teta, namber_sonic): #Угол вектора
    if 0<=teta<=90 :
	alfat = teta + list_sonic[namber_sonic]
	print '1111'
    elif 90<teta<=180:
	if namber_sonic>=5: alfat=list_sonic[namber_sonic]-(360-teta)
        else: alfat = teta + list_sonic[namber_sonic] 
    elif 180<teta<=270:
        if namber_sonic>=4: alfat=list_sonic[namber_sonic]-(360-teta)
        else: alfat = teta + list_sonic[namber_sonic] 
    elif 270<teta<=360:
        if namber_sonic>=3: alfat=list_sonic[namber_sonic]-(360-teta)
        else: alfat = teta + list_sonic[namber_sonic]
    #print alfat
    return alfat
def position(x0 ,y0, alfap, distanse):
    rad_alfap = alfap * (math.pi/180)
    y = float(y0) + (math.sin(rad_alfap)*distanse) + math.sin(rad_alfap)*12.5
    x = float(x0) + (math.cos(rad_alfap)*distanse) + math.cos(rad_alfap)*12.5
    pos=(x, y) 
    return pos

clok = pygame.time.Clock()
while True:
    for e in pygame.event.get():
        if e.type == pygame.QUIT: exit(0)
        if e.type == pygame.KEYDOWN and e.key == pygame.K_ESCAPE: exit(0)
	if e.type == pygame.KEYDOWN and e.key == pygame.K_LEFT: 
           angle += 5
           if angle >=360: angle = 0 
	if e.type == pygame.KEYDOWN and e.key == pygame.K_RIGHT: 
	   angle -= 5
           if angle <=0: angle = 360
	if e.type == pygame.KEYDOWN and e.key == pygame.K_DOWN: backward = True
	if e.type == pygame.KEYDOWN and e.key == pygame.K_UP: forward = True
	if e.type == pygame.KEYUP and e.key == pygame.K_DOWN: backward = False
	if e.type == pygame.KEYUP and e.key == pygame.K_UP: forward = False
    
    screen.fill((100, 255, 210))

    path =4
    robot.move(forward, backward, angle, path) #Движение
    pos_robot = robot.get_position() 
    ppos = position(pos_robot[0], -(pos_robot[1]), real_alige(angle, 1), 70)
    pf = Platform(ppos[0], -ppos[1])
    entities.add(pf)
    platforms.append(pf)
    robot.render(screen, (ppos[0], ppos[1]), angle) #Поворот    
    for e in entities:
       screen.blit(e.image, camera.apply(e))	
    camera.update(robot)
    pygame.display.update()
    clok.tick(30)
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы