S
S
SKAPeR2014-01-23 21:27:59
Python
SKAPeR, 2014-01-23 21:27:59

Pygame - Python math problem

Here is the code, in theory it should draw small 4x4 rectangles in front of the moving car (in the end they overlap each other and look like a line). In this case, the sprite of the car can be directed at any angle. And the problem is that this line of rectangles does not in all cases go at the same angle as the machine. When entering integer angles (0, 90, 180...), the line is displayed correctly (see figure, 3rd picture). And at angles such as 222 degrees or 333, the angle of inclination of the line and the trajectory of the machine do not match. I think that the problem is in calculating the coordinates of the rectangles, but I don’t understand where the error is.
d5e9b1180494f1cd5f0b09b3e948de9a.png

Code for 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)

Answer the question

In order to leave comments, you need to log in

3 answer(s)
N
Nicknnn, 2014-01-24
@Nicknnn

Something is wrong with the very formula for calculating the movement of the machine. If you set the angle to 45 and the speed, for example, 10, then the trajectory will already become much closer to the truth. And with a speed of 1, the car will move strictly vertically, regardless of the angle.

N
Nicknnn, 2014-01-24
@Nicknnn

Although here is an option. Store the coordinate separately.
The current needs to be finalized by get_position. Let it return the saved coordinates better.

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

S
SKAPeR, 2014-01-24
@SKAPeR

There was another small problem, as you can see from the def render() function, the rotated car is drawn to the screen via screen.blit(image, self.rect) . I need the camera to fix relative to the car and move with it, I put all the platforms in one entity array, and when the camera moves, they are drawn relative to it. Using camera.update(robot), the camera is fixed relative to the center of the screen, and all elements are displayed incorrectly ...
Probably the car should be added to the same array as the platforms, but with a rotated image, I can't do it

Camera code:
#!/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)

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question