Форум программистов, компьютерный форум, киберфорум
OpenGL
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
Рейтинг 4.56/9: Рейтинг темы: голосов - 9, средняя оценка - 4.56
175 / 10 / 1
Регистрация: 05.08.2024
Сообщений: 73

Как сделать изменение координат (или анимацию) в PyOpenGL?

12.08.2024, 16:15. Показов 4010. Ответов 35
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Код создает несколько треугольников и один синий квадрат. Как сделать так, чтобы синий квадрат мог вращаться, не изменяя своих пропорций, а также это не должно касаться других фигур? Помогите, пожалуйста.
Вот код:
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
import pygame
 
from pygame.locals import *
 
from OpenGL.GL import *
from OpenGL.GLU import *
from time import *
koordinaty = (
    (0,0,0),
    (4000,0,0),
    (6000,0,0),
    (0,3000,0),
    (2000, 3000, 0),
    (6000, 3000, 0),
    (3000, 3000, 2000),
    
    (2000, 2000, 200),
    (2000,0,200),
    (0, 2000,200),
    (0,0,200)
     )
стороны = (
    (0, 3, 4),
    (0, 4, 1),
    (1, 4, 5),
    (1, 2, 5)
    )
стороны2 = (
    (6, 3, 5),
    (6, 3, 5)
    )
 
фигура = (
    (7,8,9,10),
    (10 ,9, 8, 7)
    
    )
    
    
def Cube():
    glBegin(GL_TRIANGLES)
    for surface in стороны:
        glColor3fv((0,0,1))
        for vertex in surface:
            glVertex3fv(koordinaty[vertex])
    glEnd()     
    
    glBegin(GL_TRIANGLES)
    for surface in стороны2:
        glColor3fv((1,1,1))
        for vertex in surface:
            glVertex3fv(koordinaty[vertex])
    glEnd()    
    
    glBegin(GL_QUADS)
    for surface in фигура:
        glColor3fv((0,1,0))
        for vertex in surface:
            glVertex3fv(koordinaty[vertex])
    glEnd()      
 
def upravlenie():
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            quit()
                
        knopki = pygame.key.get_pressed()
        # W A S D
        if knopki[pygame.K_w]:
            glTranslatef(0,0, 250)
        if knopki[pygame.K_a]:
            glTranslatef(250,0, 0)
        if knopki[pygame.K_d]:
            glTranslatef(-250,0, 0)
        if knopki[pygame.K_s]:
            glTranslatef(0,0, -250)
        #SHIFT ПРОБЕЛ
        if knopki[pygame.K_RSHIFT] or knopki[pygame.K_LSHIFT]:
            glTranslatef(0,100, 0) 
        if knopki[pygame.K_SPACE]:
            glTranslatef(0,-100, 0)
        #стрелки    
        if knopki[pygame.K_LEFT]:
                glRotatef(10, 0, 2, 0)   
        if knopki[pygame.K_RIGHT]:
                glRotatef(-10, 0, 2, 0)            
                
                
def main():
                                                                      
    display = (1600,830)
    
    pygame.display.set_mode(display, DOUBLEBUF|OPENGL)
 
    gluPerspective(45, (display[0]/display[1]), 0.1,              100000.0)
    
    glTranslatef(-1000,-1000.0, -6000)
    
    glRotatef(0, 0, 0, 0)
    
    glEnable(GL_DEPTH_TEST)
      
    while True:                                            
        glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
        
        upravlenie()
        Cube()        
        
        pygame.display.flip()
 
if __name__ == '__main__':
    main()
2
Лучшие ответы (1)
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
12.08.2024, 16:15
Ответы с готовыми решениями:

Frame animation или как сделать анимацию
Здравствуйте. Подскажите, пожалуйста, как сделать анимацию? У меня есть xml файл, создал под него папку anim и там создал этот...

Как сделать анимацию или физику в консольной игре?
Всем привет) Я юный программист (мне 14) Вот пишу игру в косноле на C# что то ти по песочниц НЕ ЧЕРЕЗ МАСИВЫ. В игре можно только строить...

Как сделать анимацию двери в авто на Unity 4 или 5?
Может у кого то есть исходник? Хочу что бы игрок садился в машину и двери открывались, закрывались. Если нет, то может есть просто скрипт...

35
9991 / 2963 / 497
Регистрация: 05.10.2013
Сообщений: 8,135
Записей в блоге: 243
16.08.2024, 19:23
Студворк — интернет-сервис помощи студентам
Добавил к примеру выше матрицу вида (камеру), матрицу ортогональной проекции и scale-матрицу. В примере выше нет матрицы проекции, поэтому размер холста ограничен [-1, 1], а с помощью ортогональной матрицы проекции можно задать любой размер холста. Например, я задал от -50 до 50 по X и Y и от -10 до 10 по Z. Матрицы можно комбинировать перемножая их. В результате получается projViewMatrix:

Python
1
2
3
4
5
6
7
8
    # Создаём матрицу ортогональной проекции и вида
    projMatrix = glm.ortho(-50, 50, -50, 50, 10, -10)
    viewMatrix = glm.lookAt(
        glm.vec3(0, 0, 10), # Позиция камеры
        glm.vec3(0, 0, 0), # Точку наблюдения
        glm.vec3(0, 1, 0)) # Направление макушки камеры
    # Комбинируем матрицу проекции и матрицу вида в одну матрицу
    projViewMatrix = projMatrix * viewMatrix
Scale-матрица позволяет задать размер объекта. Удобно сделать треугольник в буфере размером от -0.5 до 0.5, чтобы его высота была единицей, тогда можно точно задавать размер треугольника с помощью scale-матрицы. В данном примере я выставил размер треугольника 20 по X и 20 по Y, а по Z в 2D лучше выставить 1, а если выставить 0 по Z, то треугольник не будет виден. После комбинирования modelMatrix и projViewMatrix получается общая mvpMatrix:

Python
1
2
3
4
5
6
7
8
9
10
11
# Создаём матрицу модели, то есть матрицу
# объединяющую матрицу перемещения и scale-матрицу
modelMatrix = glm.translate(glm.mat4(1), glm.vec3(x, y, 0))
modelMatrix = glm.scale(modelMatrix, glm.vec3(20, 20, 1))
 
# Комбинируем projView-матрицу и матрицу модели в одну MVP-матрицу
mvpMatrix = projViewMatrix * modelMatrix
 
# Отправляем MVP-матрицу в вершинный шейдер
glUniformMatrix4fv(uMvpMatrixLocation, 1, GL_FALSE,
    glm.value_ptr(mvpMatrix))


main.py

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# pip install Pygame PyOpenGL PyGLM numpy
# python main.py
 
import ctypes
 
import glm
import numpy as np
import pygame
from OpenGL.GL import *
from OpenGL.GL.shaders import *
from pygame.locals import *
 
vertexShaderSource = """
    attribute vec2 aPosition;
    uniform mat4 uMvpMatrix;
 
    void main()
    {
        gl_Position = uMvpMatrix * vec4(aPosition, 0.0, 1.0);
    }
"""
 
fragmentShaderSource = """
    void main()
    {
        gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
    }
"""
 
def main():
    pygame.init()
    winWidth, winHeight = 400, 400
    display = (winWidth, winHeight)
 
    pygame.display.set_mode(display, DOUBLEBUF | OPENGL)
 
    # Выставляем тёмно-серый цвет для очистки экрана
    glClearColor(0.2, 0.2, 0.2, 1)
 
    # Задаём три вершины треугольника против часовой стрелки
    vertPositions = np.array([
        -0.5, -0.5,
        0.5, -0.5,
        0, 0.5
    ], dtype=np.float32)
    # Создание буфера в оперативной памяти видеокарты
    vertPosBuffer = glGenBuffers(1)
    # Привязка к созданному буферу
    glBindBuffer(GL_ARRAY_BUFFER, vertPosBuffer)
    # Копирование массива вершин треугольника в буфер
    glBufferData(GL_ARRAY_BUFFER, len(vertPositions) * 4,
     vertPositions, GL_STATIC_DRAW)
 
    # Создаём объект program для взаимодействия с шейдерами
    program = compileProgram(
       compileShader(vertexShaderSource, GL_VERTEX_SHADER),
       compileShader(fragmentShaderSource, GL_FRAGMENT_SHADER))
 
    # Выбираем объект program
    glUseProgram(program)
 
    # Получаем доступ к переменной aPosition в вершинном шейдере
    aPositionLocation = glGetAttribLocation(program, "aPosition")
 
    # Получаем доступ к переменной uMvpMatrix в вершинном шейдере
    uMvpMatrixLocation = glGetUniformLocation(program, "uMvpMatrix")
 
    # Создаём матрицу ортогональной проекции и вида
    projMatrix = glm.ortho(-50, 50, -50, 50, 10, -10)
    viewMatrix = glm.lookAt(
        glm.vec3(0, 0, 10), # Позиция камеры
        glm.vec3(0, 0, 0), # Точку наблюдения
        glm.vec3(0, 1, 0)) # Направление макушки камеры
    # Комбинируем матрицу проекции и матрицу вида в одну матрицу
    projViewMatrix = projMatrix * viewMatrix
 
    # Словарь для хранения нажатых клавиш
    keys = { "left": False, "right": False, "up": False, "down": False }
 
    # Координаты треугольника
    x, y = 0, 0
 
    # Скорость перемещения треугольника
    speed = 0.5
 
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()
            if event.type == pygame.KEYDOWN:
                key = pygame.key.name(event.key)
                keys[key] = True
            if event.type == pygame.KEYUP:
                key = pygame.key.name(event.key)
                keys[key] = False
 
        if keys["left"]:
            x -= speed
        if keys["right"]:
            x += speed
        if keys["up"]:
            y += speed
        if keys["down"]:
            y -= speed
 
        # Очищаем экран заданным цветом
        glClear(GL_COLOR_BUFFER_BIT)
 
        # Привязка к буферу
        glBindBuffer(GL_ARRAY_BUFFER, vertPosBuffer)
 
        # Настройка буфера
        glEnableVertexAttribArray(aPositionLocation)
        glVertexAttribPointer(aPositionLocation, 2, GL_FLOAT, GL_FALSE, 0, ctypes.c_void_p(0))
 
        # Создаём матрицу модели, то есть матрицу
        # объединяющую матрицу перемещения и scale-матрицу
        modelMatrix = glm.translate(glm.mat4(1), glm.vec3(x, y, 0))
        modelMatrix = glm.scale(modelMatrix, glm.vec3(20, 20, 1))
 
        # Комбинируем projView-матрицу и матрицу модели в одну MVP-матрицу
        mvpMatrix = projViewMatrix * modelMatrix
 
        # Отправляем MVP-матрицу в вершинный шейдер
        glUniformMatrix4fv(uMvpMatrixLocation, 1, GL_FALSE,
            glm.value_ptr(mvpMatrix))
 
        # Вызов команды рисования, которая спровоцирует
        # вызов вершинного шейдера 3 раза - по одному для
        # каждой вершины треугольника
        glDrawArrays(GL_TRIANGLES, 0, 3)
 
        pygame.display.flip()
        pygame.time.wait(10)
 
if __name__ == "__main__":
    main()
Миниатюры
0
9991 / 2963 / 497
Регистрация: 05.10.2013
Сообщений: 8,135
Записей в блоге: 243
17.08.2024, 00:01
Цитата Сообщение от Daniil_G Посмотреть сообщение
Как сделать изменение координат (или анимацию)
Цитата Сообщение от Daniil_G Посмотреть сообщение
Как сделать так, чтобы синий квадрат мог вращаться, не изменяя своих пропорций
В примере ниже имеется и изменение координат игрока и анимация вращения. Вращаются два вентилятора и присутствует управление главным героем с помощью клавиатуры:



Надо определять касания ГГ вентиляторов. Логичнее и проще было бы передвигать главного героя, вращать вентиляторы и определять касание ГГ вентиляторов с помощью физического движка PyBox2D. Можно сделать respawn ГГ, когда он коснулся вентилятора. Например, в Unity вы бы делали именно так, то есть с привлечением физического движка. В Unity встроен Box2D. В Box2D ещё есть встроенный ray casting, то есть можно бросать луч и определять какие объекты пересёк луч. Но об этом как-нидуль потом.

Я вынес рисование прямоугольника в отдельную функцию и в отдельный файл:

rectangle.py

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import math
 
import glm
from OpenGL.GL import *
 
 
def drawRectangle(projViewMatrix, uColorLocation, uMvpMatrixLocation, position, angle, scale, color):
    # Создаём матрицу модели, то есть матрицу
    # объединяющую матрицу перемещения, вращения и scale-матрицу
    modelMatrix = glm.translate(glm.mat4(1), position)
    modelMatrix = glm.rotate(modelMatrix, math.radians(angle), glm.vec3(0, 0, 1))
    modelMatrix = glm.scale(modelMatrix, scale)
 
    # Комбинируем projView-матрицу и матрицу модели в одну MVP-матрицу
    mvpMatrix = projViewMatrix * modelMatrix
 
    # Отправляем MVP-матрицу в вершинный шейдер
    glUniformMatrix4fv(uMvpMatrixLocation, 1, GL_FALSE,
        glm.value_ptr(mvpMatrix))
 
    # Отправляем значение цвета в фрагментный шейдер
    glUniform3fv(uColorLocation, 1, glm.value_ptr(color))
 
    # Вызов команды рисования, которая спровоцирует
    # вызов вершинного шейдера 6 раз - по одному для
    # каждой вершины каждого треугольника
    glDrawArrays(GL_TRIANGLES, 0, 6)
, чтобы можно было рисовать различные объекты одной функцией:

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
        # Игрок
        drawRectangle(
            projViewMatrix = projViewMatrix,
            uColorLocation = uColorLocation,
            uMvpMatrixLocation = uMvpMatrixLocation,
            position = glm.vec3(x, y, 0),
            angle = 0,
            scale = glm.vec3(10, 15, 1),
            color = glm.vec3(0.5, 1, 0.5))
 
        # Первый вентилятор
        drawRectangle(
            projViewMatrix = projViewMatrix,
            uColorLocation = uColorLocation,
            uMvpMatrixLocation = uMvpMatrixLocation,
            position = glm.vec3(-25, 25, 0),
            angle = fanAngle1,
            scale = glm.vec3(5, 20, 1),
            color = glm.vec3(1, 0.5, 0.5))
        fanAngle1 += fanAngularSpeed1
 
        # Второй вентилятор
        drawRectangle(
            projViewMatrix = projViewMatrix,
            uColorLocation = uColorLocation,
            uMvpMatrixLocation = uMvpMatrixLocation,
            position = glm.vec3(25, 25, 0),
            angle = fanAngle2,
            scale = glm.vec3(5, 20, 1),
            color = glm.vec3(0.5, 0.5, 1))
        fanAngle2 += fanAngularSpeed2
main.py

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# pip install Pygame PyOpenGL PyGLM numpy
# python main.py
 
import ctypes
 
import glm
import numpy as np
import pygame
from OpenGL.GL import *
from OpenGL.GL.shaders import *
from pygame.locals import *
 
from rectangle import drawRectangle
 
vertexShaderSource = """
    attribute vec2 aPosition;
    uniform mat4 uMvpMatrix;
 
    void main()
    {
        gl_Position = uMvpMatrix * vec4(aPosition, 0.0, 1.0);
    }
"""
 
fragmentShaderSource = """
    uniform vec3 uColor;
    void main()
    {
        gl_FragColor = vec4(uColor, 1.0);
    }
"""
 
def main():
    pygame.init()
    winWidth, winHeight = 400, 400
    display = (winWidth, winHeight)
 
    pygame.display.set_mode(display, DOUBLEBUF | OPENGL)
 
    # Выставляем тёмно-серый цвет для очистки экрана
    glClearColor(0.2, 0.2, 0.2, 1)
 
    # Задаём три вершины треугольника против часовой стрелки
    vertPositions = np.array([
        -0.5, -0.5, # Первый треугольник
        0.5, -0.5,
        -0.5, 0.5,
        -0.5, 0.5, # Второй треугольник
        0.5, -0.5,
        0.5, 0.5,
    ], dtype=np.float32)
    # Создание буфера в оперативной памяти видеокарты
    vertPosBuffer = glGenBuffers(1)
    # Привязка к созданному буферу
    glBindBuffer(GL_ARRAY_BUFFER, vertPosBuffer)
    # Копирование массива вершин треугольника в буфер
    glBufferData(GL_ARRAY_BUFFER, len(vertPositions) * 4,
     vertPositions, GL_STATIC_DRAW)
 
    # Создаём объект program для взаимодействия с шейдерами
    program = compileProgram(
       compileShader(vertexShaderSource, GL_VERTEX_SHADER),
       compileShader(fragmentShaderSource, GL_FRAGMENT_SHADER))
 
    # Выбираем объект program
    glUseProgram(program)
 
    # Получаем доступ к переменной aPosition в вершинном шейдере
    aPositionLocation = glGetAttribLocation(program, "aPosition")
 
    # Получаем доступ к переменной uMvpMatrix в вершинном шейдере
    uMvpMatrixLocation = glGetUniformLocation(program, "uMvpMatrix")
 
    # Получаем доступ к переменной uColor в вершинном шейдере
    uColorLocation = glGetUniformLocation(program, "uColor")
 
    # Создаём матрицу ортогональной проекции и вида
    projMatrix = glm.ortho(-50, 50, -50, 50, 10, -10)
    viewMatrix = glm.lookAt(
        glm.vec3(0, 0, 10), # Позиция камеры
        glm.vec3(0, 0, 0), # Точку наблюдения
        glm.vec3(0, 1, 0)) # Направление макушки камеры
    # Комбинируем матрицу проекции и матрицу вида в одну матрицу
    projViewMatrix = projMatrix * viewMatrix
 
    # Словарь для хранения нажатых клавиш
    keys = { "left": False, "right": False, "up": False, "down": False }
 
    # Координаты игрока
    x, y = 0, 0
 
    # Скорость перемещения игрока
    playerSpeed = 0.5
    # Текущий угол и скорость вращения первого вентилятора
    fanAngle1 = 0
    fanAngularSpeed1 = 0.5
    # Текущий угол и скорость вращения второго вентилятора
    fanAngle2 = 0
    fanAngularSpeed2 = 1
 
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()
            if event.type == pygame.KEYDOWN:
                key = pygame.key.name(event.key)
                keys[key] = True
            if event.type == pygame.KEYUP:
                key = pygame.key.name(event.key)
                keys[key] = False
 
        if keys["left"]:
            x -= playerSpeed
        if keys["right"]:
            x += playerSpeed
        if keys["up"]:
            y += playerSpeed
        if keys["down"]:
            y -= playerSpeed
 
        # Очищаем экран заданным цветом
        glClear(GL_COLOR_BUFFER_BIT)
 
        # Привязка к буферу
        glBindBuffer(GL_ARRAY_BUFFER, vertPosBuffer)
 
        # Настройка буфера
        glEnableVertexAttribArray(aPositionLocation)
        glVertexAttribPointer(aPositionLocation, 2, GL_FLOAT, GL_FALSE, 0, ctypes.c_void_p(0))
 
        # Игрок
        drawRectangle(
            projViewMatrix = projViewMatrix,
            uColorLocation = uColorLocation,
            uMvpMatrixLocation = uMvpMatrixLocation,
            position = glm.vec3(x, y, 0),
            angle = 0,
            scale = glm.vec3(10, 15, 1),
            color = glm.vec3(0.5, 1, 0.5))
 
        # Первый вентилятор
        drawRectangle(
            projViewMatrix = projViewMatrix,
            uColorLocation = uColorLocation,
            uMvpMatrixLocation = uMvpMatrixLocation,
            position = glm.vec3(-25, 25, 0),
            angle = fanAngle1,
            scale = glm.vec3(5, 20, 1),
            color = glm.vec3(1, 0.5, 0.5))
        fanAngle1 += fanAngularSpeed1
 
        # Второй вентилятор
        drawRectangle(
            projViewMatrix = projViewMatrix,
            uColorLocation = uColorLocation,
            uMvpMatrixLocation = uMvpMatrixLocation,
            position = glm.vec3(25, 25, 0),
            angle = fanAngle2,
            scale = glm.vec3(5, 20, 1),
            color = glm.vec3(0.5, 0.5, 1))
        fanAngle2 += fanAngularSpeed2
 
        pygame.display.flip()
        pygame.time.wait(10)
 
if __name__ == "__main__":
    main()
Миниатюры
0
175 / 10 / 1
Регистрация: 05.08.2024
Сообщений: 73
17.08.2024, 10:49  [ТС]
Здравствуйте! Я полностью изучил вашу лекцию о шейдерах в PyOpenGL. Все было отлично, понятно, подробно расписано, я понял работу шейдеров и взаимодействии с ними по вашему примеру с треугольником. Ознакомившись с теорией я решил "пощупать" сам код, но у меня возникла проблема с glw. Программа выдала ошибку:

translationMatrix = glm.translate(glm.mat4(1), glm.vec3(x, y, 0))
builtins.AttributeError: module 'glm' has no attribute 'translate'

Я попробовал разобраться в библиотеке, но ничего сделать не смог.
1
9991 / 2963 / 497
Регистрация: 05.10.2013
Сообщений: 8,135
Записей в блоге: 243
17.08.2024, 12:37
Не могу понять почему так. Я сначала подумал, что в слове "translate" возможно одну из букв "a" вы случайно набрали русской буквой, но я скопировал вашу строку кода и она работает в консоли. Следующие команды работают у вас в консоли? Откройте консоль и наберите: py чтобы запустить интерпретатор Python. Далее, введите следующие команды:

Code
1
2
3
4
5
6
7
>>> import glm
>>> translationMatrix = glm.translate(glm.mat4(1), glm.vec3(0, 0, 0))
>>> print(translationMatrix)
[            1 ][            0 ][            0 ][            0 ]
[            0 ][            1 ][            0 ][            0 ]
[            0 ][            0 ][            1 ][            0 ]
[            0 ][            0 ][            0 ][            1 ]
Добавлено через 5 минут
Скорее всего, вы установили glm вместо PyGLM. Я попробовал нагуглить текст ошибки:

builtins.AttributeError: module 'glm' has no attribute 'translate'
По первой ссылке есть очень похожая тема на Stack Overflow: AttributeError: module 'glm' has no attribute 'perspective'

Там выдвигают предположение:

It might be probably because you have downloaded and installed glm - which is a different library, instead of PyGLM.
Перевод Google Translate:

Вероятно, это связано с тем, что вы загрузили и установили glm — другую библиотеку, а не PyGLM.
Попробуйте удалить glm:

Code
1
pip uninstall glm
И установите PyGLM:

Code
1
pip install PyGLM
0
175 / 10 / 1
Регистрация: 05.08.2024
Сообщений: 73
17.08.2024, 13:28  [ТС]
Сделал все по вашей инструкции, помогло, код заработал. Теперь я более подробно могу изучить работу шейдера в PyOpenGL, а также практиковаться с ними
1
9991 / 2963 / 497
Регистрация: 05.10.2013
Сообщений: 8,135
Записей в блоге: 243
17.08.2024, 13:56
Попробуйте решить эту задачу: Для изображения осуществить 5 его выводов каждый раз обрезая высоту на 20% Если нужен пример, как накладывать текстуру, то постараюсь объяснить своими словами с примером. Только надо будет новую тему вам создать про наложение текстуры.
1
175 / 10 / 1
Регистрация: 05.08.2024
Сообщений: 73
17.08.2024, 16:14  [ТС]
Хорошо, попробую. Постараюсь сделать самостоятельно. Спасибо вам большое за столь огромную помощь в познании OpenGL с PyGame
1
9991 / 2963 / 497
Регистрация: 05.10.2013
Сообщений: 8,135
Записей в блоге: 243
17.08.2024, 18:37
Скину пример с текстурой, а если возникнет проблема с пониманием текстурных координат или интерполяции текстурных координат между вершинами с помощью varying между вершинным и фрагментным шейдерами, то сначала погуглите, а потом создавайте тему. В следующим примере для загрузки файла PNG используется пакет Pillow Устанавливается командой: pip install Pillow

Code
1
pip install Pygame PyOpenGL PyGLM numpy Pillow
В прикреплённом примере выводятся 3 прямоугольника с разными текстурами на них. Я взял эти текстуры из следующего туториала по LibGDX, где используют Box2D и который продолжается 20 часов, но в нём описан чистый Box2D и полностью создаётся игра с нуля и есть все ресурсы для этого (звуки, sprite sheets, спрайты для GUI и т.д.): The Complete LibGDX Game Course Using Java - Awesome Tuts - 20h 42m - 3.80 GB (этот курс есть там, где можно найти) Можно смотреть и переводить на Pygame и PyOpenGL ради самообучения.



Миниатюры
Вложения
Тип файла: zip jack-the-giant-textures-opengles20-pygame-python.zip (367.8 Кб, 15 просмотров)
0
9991 / 2963 / 497
Регистрация: 05.10.2013
Сообщений: 8,135
Записей в блоге: 243
19.08.2024, 00:05
Это неправильный способ изменения координат ГГ. Надо изменять их с помощью Box2D. Сложно будет рассчитать пересечение двух прямоугольников один из которых вращается. По сути вам придётся писать свой физический движок, а это куча времени и сил. Отложим ненадолго OpenGL и реализуем перемещение ГГ на Box2D с рисованием объектов с помощью Pygame, а потом несложно будет переписать рисование на OpenGL. Почему очень важно изучение Box2D. Это сэкономит много времени, чтобы не писать своё с нуля. Эти знания пригодятся если вы решите взять Unity, Godot, Cocos и т.д. Потому что в этих движках либо обёртка над Box2D, либо очень похожие другие физические движки, которые очень похожи между собой. Взять, например, Rapier2D или Matter.js - все они очень похожи на Box2D. В Box2D вам нужно будет поставить коллайдеры на объекты и на ГГ и там очень просто пишется обработчики коллизий. По сути он уже там есть. Вы легко определите какие два объекта коснулись друг друга. Если вы коснулись врага, то отнимаем сердечко жизни, если подобрали аптечку, то прибавляем сердечко жизни. А OpenGL тоже важно изучать и важно писать свои маленькие движки для маленьких игр в свободное время, чтобы изучать на практике, как работает компьютерная графика на низком уровне, а ещё на нём можете изучать математику: строить графики, рисовать формулы, решать геометрические задачи в 2D и 3D, решать задачи линейной алгебры с визуализацией и анимацией и т.д. Потом ещё надо подключить OpenAL для работы со звуками. Это тоже полезно для настройки звука и игровых движках. Параллельно изучайте Godot. У него очень похожий язык на Python, называется GDScript. Там редактор кода встроен в движок. Устанавливать его не надо. Скачиваете EXE и открывается движок. Пройдите сначала этот короткий официальный туториал: Step by step, а потом этот Your first 2D game Можете ещё пройти этот для 3D: Your first 3D game Очень важно изучать, как делаются игры на высоком уровне абстракции.

Теперь по поводу перемещения на Box2D и Pygame. Ставьте PyBox2D командой:

pip install Box2D
Да, в команде установки нужно писать Box2D, а не PyBox2D. Первое, что нужно сделать в программе - это создать физический мир, куда будут добавляться коллайдеры вокруг игрока, препятствий, врагом, монеток, аптечек и т.д. Следующий код создаём физический мир с нулевой гравитацией и выводит гравитацию в консоль:

main.py

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# pip install Box2D Pygame
 
import math
 
import pygame
from Box2D import b2Vec2, b2World
 
 
def main():
 
    pygame.init()
 
    # Создаём окно заданного размера с заголовком
    window = pygame.display.set_mode((270, 270))
    pygame.display.set_caption("Box2D")
 
    # Создаём физический мир с нулевой гравитацией
    gravity=b2Vec2(0, 0)
    world = b2World(gravity)
 
    # Выводим значение гравитации в консоль
    print(world.gravity)
 
    # Главный цикл
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()
 
if __name__ == '__main__':
    main()
Создаём коллайдер ГГ, которым будет управлять стрелками клавиатуры. Физический объект состоит из тела (Body), формы (Shape) и есть ещё одна сущность, которая называется Fixture - она соединяет тело и форму и обладает определёнными свойствами, которые пока не важны. Объект может быть статическим (b2_staticBody), например, стены, а может быть динамическим (b2_dynamicBody) и ещё кинематический тип, но это потом. По умолчанию объекты имеют статический тип. Я написал в комментария, что означает каждая строка:

main.py

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# pip install Box2D Pygame
 
import math
 
import pygame
from Box2D import (b2_dynamicBody, b2BodyDef, b2FixtureDef, b2PolygonShape,
                   b2Vec2, b2World)
 
 
def main():
 
    pygame.init()
 
    # Создаём окно заданного размера с заголовком
    window = pygame.display.set_mode((270, 270))
    pygame.display.set_caption("Box2D")
 
    # Создаём физический мир с нулевой гравитацией
    gravity=b2Vec2(0, 0)
    world = b2World(gravity)
 
    # Размер физического мира должен быть от 0.1 до 10
    # поэтому создаётся константа pixelsPerMeter для
    # перевода из мира пикселей в физический мир Box2D
    pixelsPerMeter = 30
 
    # Создание тела игрока
    playerBodyDef = b2BodyDef() # Объект для настройки позиции и типа объекта
    # Задаём позицию и тип игрока
    playerBodyDef.position = b2Vec2(100 / pixelsPerMeter, 30 / pixelsPerMeter)
    playerBodyDef.type = b2_dynamicBody
    # Создаём тело игрока
    playerBody = world.CreateBody(playerBodyDef)
    # Создаём форму игрока в виде прямоугольника
    playerShape = b2PolygonShape()
    playerShape.SetAsBox(30 / pixelsPerMeter, 30 / pixelsPerMeter)
    # Связываем тело игрока и форму
    playerFixtureDef = b2FixtureDef()
    playerFixtureDef.shape = playerShape
    # Плотность игрока. Это аналог массы
    playerFixtureDef.density = 1
    playerBody.CreateFixture(playerFixtureDef)
 
    # Главный цикл
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()
 
if __name__ == '__main__':
    main()
Если вы запустите код выше, то коллайдер ГГ не будет виден. Для отображения коллайдеров в Box2D есть класс b2Draw, который можно наследовать. Нужно переопределить методы этого класса. Нам нужен метод DrawSolidPolygon(vertices, color), который даёт в своём теле доступ к координатам вершин прямоугольного коллайдера, а так же он даёт цвет. Мы может нарисовать линии в файле debug_drawer.py, а в файле main.py нужно создать объект класса DebugDrawer и настоить его:

Python
1
2
3
4
# Создаём DebugDrawer настраиваем его
debugDrawer = DebugDrawer(window, pixelsPerMeter)
debugDrawer.flags = { "drawShapes": True }
world.renderer = debugDrawer
В цикле рисования нужно вызвать метод рисования коллайдеров на world:

Python
1
2
# Рисуем коллайдеры
world.DrawDebugData()
Будет нарисован коллайдер ГГ:

Название: ca9ba37a7e01e3acf4ee6b96a8ae97eb475dab91.png
Просмотров: 145

Размер: 1.9 Кб

main.py

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# pip install Box2D Pygame
 
import math
 
import pygame
from Box2D import (b2_dynamicBody, b2BodyDef, b2FixtureDef, b2PolygonShape,
                   b2Vec2, b2World)
 
from debug_drawer import DebugDrawer
 
 
def main():
 
    pygame.init()
 
    # Создаём окно заданного размера с заголовком
    window = pygame.display.set_mode((268, 268))
    pygame.display.set_caption("PyGame Box2D")
 
    # Создаём физический мир с нулевой гравитацией
    gravity=b2Vec2(0, 0)
    world = b2World(gravity)
 
    # Размер физического мира должен быть от 0.1 до 10
    # поэтому создаётся константа pixelsPerMeter для
    # перевода из мира пикселей в физический мир Box2D
    pixelsPerMeter = 30
 
    # Создаём DebugDrawer настраиваем его
    debugDrawer = DebugDrawer(window, pixelsPerMeter)
    debugDrawer.flags = { "drawShapes": True }
    world.renderer = debugDrawer
 
    # Создание тела игрока
    playerBodyDef = b2BodyDef() # Объект для настройки позиции и типа объекта
    # Задаём позицию и тип игрока
    playerBodyDef.position = b2Vec2(100 / pixelsPerMeter, 100 / pixelsPerMeter)
    playerBodyDef.type = b2_dynamicBody
    # Создаём тело игрока
    playerBody = world.CreateBody(playerBodyDef)
    # Создаём форму игрока в виде прямоугольника
    playerShape = b2PolygonShape()
    playerShape.SetAsBox(20 / pixelsPerMeter, 30 / pixelsPerMeter)
    # Связываем тело игрока и форму
    playerFixtureDef = b2FixtureDef()
    playerFixtureDef.shape = playerShape
    # Плотность игрока. Это аналог массы
    playerFixtureDef.density = 1
    playerBody.CreateFixture(playerFixtureDef)
 
    # Главный цикл
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()
 
        # Заливаем экран чёрным цветом
        window.fill(0)
 
        # Рисуем коллайдеры
        world.DrawDebugData()
 
        # Обновляем отображение
        pygame.display.flip()
 
if __name__ == '__main__':
    main()
debug_drawer.py

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import math
 
import pygame
from Box2D import b2Draw
 
 
class DebugDrawer(b2Draw):
 
    def __init__(self, window, pixelsPerMeter, thickness=3):
        super().__init__()
        self.window = window
        self.pixelsPerMeter = pixelsPerMeter
        self.thickness = thickness
 
    def DrawSolidPolygon(self, vertices, color):
        pygame.draw.line(self.window, (color.r * 255, color.g * 255, color.b * 255),
            (vertices[0][0] * self.pixelsPerMeter, vertices[0][1] * self.pixelsPerMeter),
            (vertices[1][0] * self.pixelsPerMeter, vertices[1][1] * self.pixelsPerMeter),
            self.thickness)
        pygame.draw.line(self.window, (color.r * 255, color.g * 255, color.b * 255),
            (vertices[1][0] * self.pixelsPerMeter, vertices[1][1] * self.pixelsPerMeter),
            (vertices[2][0] * self.pixelsPerMeter, vertices[2][1] * self.pixelsPerMeter),
            self.thickness)
        pygame.draw.line(self.window, (color.r * 255, color.g * 255, color.b * 255),
            (vertices[2][0] * self.pixelsPerMeter, vertices[2][1] * self.pixelsPerMeter),
            (vertices[3][0] * self.pixelsPerMeter, vertices[3][1] * self.pixelsPerMeter),
            self.thickness)
        pygame.draw.line(self.window, (color.r * 255, color.g * 255, color.b * 255),
            (vertices[3][0] * self.pixelsPerMeter, vertices[3][1] * self.pixelsPerMeter),
            (vertices[0][0] * self.pixelsPerMeter, vertices[0][1] * self.pixelsPerMeter),
            self.thickness)
 
    def DrawSolidCircle(self, center, radius, axis, color):
        pass
    def DrawPolygon(self, vertices, color):
        pass
    def DrawSegment(self, p1, p2, color):
        pass
    def DrawPoint(self, p, size, color):
        pass
    def DrawCircle(self, center, radius, color, drawwidth=1):
        pass
    def DrawTransform(self, xf):
        pass
1
9991 / 2963 / 497
Регистрация: 05.10.2013
Сообщений: 8,135
Записей в блоге: 243
19.08.2024, 02:12
Добавил коллайдер стены повёрнутой под углом 30 градусов по аналогии с коллайдером игрока. Добавил перемещение игрока:

Название: wall-box2d-pygame-python.gif
Просмотров: 145

Размер: 21.2 Кб

Запретил игроку засыпать, потому что иначе его нельзя сдвинуть после того, как он заснул через несколько секунд неактивности:

Python
1
2
# Игрок не должен засыпать
playerBody.sleepingAllowed = False
Запретил вращение:

Python
1
2
    # Запрещаем игроку вращение
    playerBodyDef.fixedRotation = True
Добавил в главный цикл расчёт промежутка времени между кадрами, который требует шаг симуляции Box2D:

Python
1
2
3
4
5
6
    # Установить количество кадров в секунду (60) и
        # считать промежуток времени между кадрами dt (~0.16)
        dt = clock.tick(60) / 1000
 
        # Продвинуть физическую симуляцию на один шаг
        world.Step(dt, 3, 2)
Задаю линейную скорость при нажатии клавиш-стрелок:

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()
            if event.type == pygame.KEYDOWN:
                key = pygame.key.name(event.key)
                keys[key] = True
            if event.type == pygame.KEYUP:
                key = pygame.key.name(event.key)
                keys[key] = False
 
        if keys["left"]:
            playerBody.linearVelocity.x = -playerSpeed
        if keys["right"]:
            playerBody.linearVelocity.x = playerSpeed
        if keys["up"]:
            playerBody.linearVelocity.y = -playerSpeed
        if keys["down"]:
            playerBody.linearVelocity.y = playerSpeed
Останавливаю игрока после шага симуляции world.Step():

Python
1
2
3
4
5
        # Продвинуть физическую симуляцию на один шаг
        world.Step(dt, 3, 2)
 
        # Останавливаем игрока
        playerBody.linearVelocity = b2Vec2(0, 0)
Весь код:

main.py

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# pip install Box2D Pygame
 
import math
 
import pygame
from Box2D import (b2_dynamicBody, b2_staticBody, b2BodyDef, b2FixtureDef,
                   b2PolygonShape, b2Vec2, b2World)
 
from debug_drawer import DebugDrawer
 
 
def main():
 
    pygame.init()
 
    # Создаём окно заданного размера с заголовком
    window = pygame.display.set_mode((268, 268))
    pygame.display.set_caption("PyGame Box2D")
 
    # Создаём физический мир с нулевой гравитацией
    gravity=b2Vec2(0, 0)
    world = b2World(gravity)
 
    # Размер физического мира должен быть от 0.1 до 10
    # поэтому создаётся константа pixelsPerMeter для
    # перевода из мира пикселей в физический мир Box2D
    pixelsPerMeter = 30
 
    # Создаём DebugDrawer настраиваем его
    debugDrawer = DebugDrawer(window, pixelsPerMeter)
    debugDrawer.flags = { "drawShapes": True }
    world.renderer = debugDrawer
 
    # Создание тела игрока
    playerBodyDef = b2BodyDef() # Объект для настройки позиции и типа объекта
    # Задаём позицию и тип игрока
    playerBodyDef.position = b2Vec2(200 / pixelsPerMeter, 100 / pixelsPerMeter)
    playerBodyDef.type = b2_dynamicBody
    # Запрещаем игроку вращение
    playerBodyDef.fixedRotation = True
    # Создаём тело игрока
    playerBody = world.CreateBody(playerBodyDef)
    # Игрок не должен засыпать
    playerBody.sleepingAllowed = False
    # Создаём форму игрока в виде прямоугольника
    playerShape = b2PolygonShape()
    playerShape.SetAsBox(10 / pixelsPerMeter, 20 / pixelsPerMeter)
    # Связываем тело игрока и форму
    playerFixtureDef = b2FixtureDef()
    playerFixtureDef.shape = playerShape
    # Плотность игрока. Это аналог массы
    playerFixtureDef.density = 1
    playerBody.CreateFixture(playerFixtureDef)
 
    # Создаём стену по аналогии с игроком, но тип
    # тела статический и density = 0
    wallBodyDef = b2BodyDef()
    # Задаём позицию и тип стены
    wallBodyDef.position = b2Vec2(100 / pixelsPerMeter, 150 / pixelsPerMeter)
    wallBodyDef.type = b2_staticBody
    wallBodyDef.angle = math.radians(30)
    # Создаём тело стены
    wallBody = world.CreateBody(wallBodyDef)
    # Создаём форму игрока в виде прямоугольника
    wallShape = b2PolygonShape()
    wallShape.SetAsBox(10 / pixelsPerMeter, 50 / pixelsPerMeter)
    # Связываем тело стены и форму
    wallFixtureDef = b2FixtureDef()
    wallFixtureDef.shape = wallShape
    # Плотность стены. Это аналог массы
    wallFixtureDef.density = 0
    wallBody.CreateFixture(wallFixtureDef)
 
    # Словарь для хранения нажатых клавиш
    keys = { "left": False, "right": False, "up": False, "down": False }
 
    clock = pygame.time.Clock()
    playerSpeed = 5
 
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()
            if event.type == pygame.KEYDOWN:
                key = pygame.key.name(event.key)
                keys[key] = True
            if event.type == pygame.KEYUP:
                key = pygame.key.name(event.key)
                keys[key] = False
 
        if keys["left"]:
            playerBody.linearVelocity.x = -playerSpeed
        if keys["right"]:
            playerBody.linearVelocity.x = playerSpeed
        if keys["up"]:
            playerBody.linearVelocity.y = -playerSpeed
        if keys["down"]:
            playerBody.linearVelocity.y = playerSpeed
 
        # Установить количество кадров в секунду (60) и
        # считать промежуток времени между кадрами dt (~0.16)
        dt = clock.tick(60) / 1000
 
        # Продвинуть физическую симуляцию на один шаг
        world.Step(dt, 3, 2)
 
        # Останавливаем игрока
        playerBody.linearVelocity = b2Vec2(0, 0)
 
        # Заливаем экран чёрным цветом
        window.fill(0)
 
        # Рисуем коллайдеры
        world.DrawDebugData()
 
        # Обновляем отображение
        pygame.display.flip()
 
if __name__ == '__main__':
    main()
debug_drawer.py

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import math
 
import pygame
from Box2D import b2Draw
 
 
class DebugDrawer(b2Draw):
 
    def __init__(self, window, pixelsPerMeter, thickness=3):
        super().__init__()
        self.window = window
        self.pixelsPerMeter = pixelsPerMeter
        self.thickness = thickness
 
    def DrawSolidPolygon(self, vertices, color):
        pygame.draw.line(self.window, (color.r * 255, color.g * 255, color.b * 255),
            (vertices[0][0] * self.pixelsPerMeter, vertices[0][1] * self.pixelsPerMeter),
            (vertices[1][0] * self.pixelsPerMeter, vertices[1][1] * self.pixelsPerMeter),
            self.thickness)
        pygame.draw.line(self.window, (color.r * 255, color.g * 255, color.b * 255),
            (vertices[1][0] * self.pixelsPerMeter, vertices[1][1] * self.pixelsPerMeter),
            (vertices[2][0] * self.pixelsPerMeter, vertices[2][1] * self.pixelsPerMeter),
            self.thickness)
        pygame.draw.line(self.window, (color.r * 255, color.g * 255, color.b * 255),
            (vertices[2][0] * self.pixelsPerMeter, vertices[2][1] * self.pixelsPerMeter),
            (vertices[3][0] * self.pixelsPerMeter, vertices[3][1] * self.pixelsPerMeter),
            self.thickness)
        pygame.draw.line(self.window, (color.r * 255, color.g * 255, color.b * 255),
            (vertices[3][0] * self.pixelsPerMeter, vertices[3][1] * self.pixelsPerMeter),
            (vertices[0][0] * self.pixelsPerMeter, vertices[0][1] * self.pixelsPerMeter),
            self.thickness)
 
    def DrawSolidCircle(self, center, radius, axis, color):
        pass
    def DrawPolygon(self, vertices, color):
        pass
    def DrawSegment(self, p1, p2, color):
        pass
    def DrawPoint(self, p, size, color):
        pass
    def DrawCircle(self, center, radius, color, drawwidth=1):
        pass
    def DrawTransform(self, xf):
        pass
1
9991 / 2963 / 497
Регистрация: 05.10.2013
Сообщений: 8,135
Записей в блоге: 243
19.08.2024, 03:08
Добавил вентилятор типа b2_kinematicBody. Этот тип, в отличие от b2_dynamicBody, не управляется силами или импульсами. Их можно напрямую двигать и вращать. Это хорошо подходит, например, для лифтов или движущихся платформ. В данном случае он отлично подходит для вентилятора. Итого получается в этом примере есть все типы тел: b2_dynamicBody (игрок), b2_staticBody (стена) и b2_kinematicBody (вентилятор) Добавил имена для всех объектов, чтобы выводить их в консоль когда произошла коллизия игрока со стеной и вентилятором:

Python
1
2
3
playerFixture.userData = { "name": "player" }
wallFixture.userData = { "name": "wall" }
fanFixture.userData = { "name": "fan" }
Добавил класс ContactListener, который унаследовал от b2ContactListener. Здесь переопределил один метод - BeginContact, который срабатывает, когда происходит контакт между двумя коллайдерами. Выводятся имена в консоль при контакте:

Python
1
2
3
4
5
6
class ContactListener(b2ContactListener):
 
    def BeginContact(self, contact):
        userDataA = contact.fixtureA.userData
        userDataB = contact.fixtureB.userData
        print(userDataA["name"], "<--->", userDataB["name"])
Экземпляр класса ContactListener нужно присвоить атрибуту contactListener физического мира:

Python
1
2
# Создаём ContactListener
world.contactListener = ContactListener()
Ничего самому рассчитывать не нужно для определения контакта, Box2D это делает сам:



main.py

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# pip install Box2D Pygame
 
import math
 
import pygame
from Box2D import (b2_dynamicBody, b2_kinematicBody, b2_staticBody, b2BodyDef,
                   b2ContactListener, b2FixtureDef, b2PolygonShape, b2Vec2,
                   b2World)
 
from debug_drawer import DebugDrawer
 
 
class ContactListener(b2ContactListener):
 
    def BeginContact(self, contact):
        userDataA = contact.fixtureA.userData
        userDataB = contact.fixtureB.userData
        print(userDataA["name"], "<--->", userDataB["name"])
 
def main():
 
    pygame.init()
 
    # Создаём окно заданного размера с заголовком
    window = pygame.display.set_mode((268, 268))
    pygame.display.set_caption("PyGame Box2D")
 
    # Создаём физический мир с нулевой гравитацией
    gravity=b2Vec2(0, 0)
    world = b2World(gravity)
 
    # Размер физического мира должен быть от 0.1 до 10
    # поэтому создаётся константа pixelsPerMeter для
    # перевода из мира пикселей в физический мир Box2D
    pixelsPerMeter = 30
 
    # Создаём DebugDrawer настраиваем его
    debugDrawer = DebugDrawer(window, pixelsPerMeter)
    debugDrawer.flags = { "drawShapes": True }
    world.renderer = debugDrawer
 
    # Создаём ContactListener
    world.contactListener = ContactListener()
 
    # Создание тела игрока
    playerBodyDef = b2BodyDef() # Объект для настройки позиции и типа объекта
    # Задаём позицию и тип игрока
    playerBodyDef.position = b2Vec2(200 / pixelsPerMeter, 100 / pixelsPerMeter)
    playerBodyDef.type = b2_dynamicBody
    # Запрещаем игроку вращение
    playerBodyDef.fixedRotation = True
    # Создаём тело игрока
    playerBody = world.CreateBody(playerBodyDef)
    # Игрок не должен засыпать
    playerBody.sleepingAllowed = False
    # Создаём форму игрока в виде прямоугольника
    playerShape = b2PolygonShape()
    playerShape.SetAsBox(10 / pixelsPerMeter, 20 / pixelsPerMeter)
    # Связываем тело игрока и форму
    playerFixtureDef = b2FixtureDef()
    playerFixtureDef.shape = playerShape
    # Плотность игрока. Это аналог массы
    playerFixtureDef.density = 1
    playerFixture = playerBody.CreateFixture(playerFixtureDef)
    playerFixture.userData = { "name": "player" }
 
    # Создаём стену по аналогии с игроком, но тип
    # тела статический и density = 0
    wallBodyDef = b2BodyDef()
    # Задаём позицию и тип стены
    wallBodyDef.position = b2Vec2(100 / pixelsPerMeter, 150 / pixelsPerMeter)
    wallBodyDef.type = b2_staticBody
    wallBodyDef.angle = math.radians(30)
    # Создаём тело стены
    wallBody = world.CreateBody(wallBodyDef)
    # Создаём форму игрока в виде прямоугольника
    wallShape = b2PolygonShape()
    wallShape.SetAsBox(10 / pixelsPerMeter, 50 / pixelsPerMeter)
    # Связываем тело стены и форму
    wallFixtureDef = b2FixtureDef()
    wallFixtureDef.shape = wallShape
    # Плотность стены. Это аналог массы
    wallFixtureDef.density = 0
    wallFixture = wallBody.CreateFixture(wallFixtureDef)
    wallFixture.userData = { "name": "wall" }
 
    # Создаём вентилятор кинематического типа
    # тела статический и density = 0
    fanBodyDef = b2BodyDef()
    # Задаём позицию и тип вентилятора
    fanBodyDef.position = b2Vec2(200 / pixelsPerMeter, 200 / pixelsPerMeter)
    fanBodyDef.type = b2_kinematicBody
    fanBodyDef.angle = math.radians(0)
    # Создаём тело вентилятора
    fanBody = world.CreateBody(fanBodyDef)
    # Создаём форму вентилятора в виде прямоугольника
    fanShape = b2PolygonShape()
    fanShape.SetAsBox(5 / pixelsPerMeter, 30 / pixelsPerMeter)
    # Связываем тело вентилятора и форму
    fanFixtureDef = b2FixtureDef()
    fanFixtureDef.shape = fanShape
    # Плотность вентилятора. Это аналог массы
    fanFixtureDef.density = 0
    fanFixture = fanBody.CreateFixture(fanFixtureDef)
    fanFixture.userData = { "name": "fan" }
 
    # Словарь для хранения нажатых клавиш
    keys = { "left": False, "right": False, "up": False, "down": False }
 
    clock = pygame.time.Clock()
    playerSpeed = 5
    fanSpeed = 180
 
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()
            if event.type == pygame.KEYDOWN:
                key = pygame.key.name(event.key)
                keys[key] = True
            if event.type == pygame.KEYUP:
                key = pygame.key.name(event.key)
                keys[key] = False
 
        if keys["left"]:
            playerBody.linearVelocity.x = -playerSpeed
        if keys["right"]:
            playerBody.linearVelocity.x = playerSpeed
        if keys["up"]:
            playerBody.linearVelocity.y = -playerSpeed
        if keys["down"]:
            playerBody.linearVelocity.y = playerSpeed
 
        # Установить количество кадров в секунду (60) и
        # считать промежуток времени между кадрами dt (~0.16)
        dt = clock.tick(60) / 1000
 
        # Задать угол вентилятору
        fanBody.angle += math.radians(fanSpeed * dt)
 
        # Продвинуть физическую симуляцию на один шаг
        world.Step(dt, 3, 2)
 
        # Останавливаем игрока
        playerBody.linearVelocity = b2Vec2(0, 0)
 
        # Заливаем экран чёрным цветом
        window.fill(0)
 
        # Рисуем коллайдеры
        world.DrawDebugData()
 
        # Обновляем отображение
        pygame.display.flip()
 
if __name__ == '__main__':
    main()
debug_drawer.py

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import math
 
import pygame
from Box2D import b2Draw
 
 
class DebugDrawer(b2Draw):
 
    def __init__(self, window, pixelsPerMeter, thickness=3):
        super().__init__()
        self.window = window
        self.pixelsPerMeter = pixelsPerMeter
        self.thickness = thickness
 
    def DrawSolidPolygon(self, vertices, color):
        pygame.draw.line(self.window, (color.r * 255, color.g * 255, color.b * 255),
            (vertices[0][0] * self.pixelsPerMeter, vertices[0][1] * self.pixelsPerMeter),
            (vertices[1][0] * self.pixelsPerMeter, vertices[1][1] * self.pixelsPerMeter),
            self.thickness)
        pygame.draw.line(self.window, (color.r * 255, color.g * 255, color.b * 255),
            (vertices[1][0] * self.pixelsPerMeter, vertices[1][1] * self.pixelsPerMeter),
            (vertices[2][0] * self.pixelsPerMeter, vertices[2][1] * self.pixelsPerMeter),
            self.thickness)
        pygame.draw.line(self.window, (color.r * 255, color.g * 255, color.b * 255),
            (vertices[2][0] * self.pixelsPerMeter, vertices[2][1] * self.pixelsPerMeter),
            (vertices[3][0] * self.pixelsPerMeter, vertices[3][1] * self.pixelsPerMeter),
            self.thickness)
        pygame.draw.line(self.window, (color.r * 255, color.g * 255, color.b * 255),
            (vertices[3][0] * self.pixelsPerMeter, vertices[3][1] * self.pixelsPerMeter),
            (vertices[0][0] * self.pixelsPerMeter, vertices[0][1] * self.pixelsPerMeter),
            self.thickness)
 
    def DrawSolidCircle(self, center, radius, axis, color):
        pass
    def DrawPolygon(self, vertices, color):
        pass
    def DrawSegment(self, p1, p2, color):
        pass
    def DrawPoint(self, p, size, color):
        pass
    def DrawCircle(self, center, radius, color, drawwidth=1):
        pass
    def DrawTransform(self, xf):
        pass
Миниатюры
1
9991 / 2963 / 497
Регистрация: 05.10.2013
Сообщений: 8,135
Записей в блоге: 243
20.08.2024, 15:28
Глядя на пример, выше возникает вопрос: как на OpenGL нарисовать отрезок заданной толщины, имея координаты точки начала и конца отрезка? В примере выше используется метод pygame.draw.line() для рисования отрезка между двух указанных точек.

Эта функция принимает:
  • start_pos - координаты точки начала отрезка
  • end_pos - координаты точки конца отрезка
  • width - толщину отрезка
  • color - цвет в виде набора RGB

Важнейшая задача написать функцию с таким же функционалом на OpenGL. Это откроет множество возможностей. Вы сможете рисовать с помощью отрезков: коллайдеры, стрелки, оси координат, ломанные, графики функций и т.д. В местах сочленения ломанных можно помещать закрашенные окружности, чтобы они были гладкими. Для рисования отрезка я создаю квадрат в буфере размером 1x1, то есть:

Python
1
2
3
4
5
6
7
# Задаём вершины квадрата
vertPositions = np.array([
    -0.5, -0.5,
    0.5, -0.5,
    -0.5, 0.5,
    0.5, 0.5
], dtype=np.float32)
В функции glDrawArrays() нужно в параметрах написать GL_TRIANGLE_STRIP и 4 вершины:

Python
1
2
3
4
# Вызов команды рисования, которая спровоцирует
# вызов вершинного шейдера 4 раза - по одному для
# каждой вершины квадрата
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)
Имея две точки начала и конца отрезка можно рассчитать середину отрезка, угол поворота и длину отрезка. Ширину отрезка мы задаём сами. Эти данные я использую, чтобы трансформировать квадрат для получения отрезка. Самое сложное - рассчитать угол имея две точки. Подумайте, как найти этот угол. Подсказка - тангенс угла - это отношения противолежащего катета с прилежащему. Отсюда угол. Напишите на Pyhon, как найти арктангенс, то есть имея тангенс найти угол.
1
175 / 10 / 1
Регистрация: 05.08.2024
Сообщений: 73
20.08.2024, 20:33  [ТС]
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
import pygame as pg 
import numpy as np 
from OpenGL.GL import *
# Импортируем функции, упрощающие работу с шейдерами
from OpenGL.GL.shaders import compileShader, compileProgram
 
#создание координат вершин и их цвета, которые будут отрисованы шейдерами
koordinaty = [
 #x  y  z    r        g        b
 -1, 0.5, 0, #52/255, 178/255, 152/255,
 0, -0.5, 0, #57/255, 151/255, 211/255,
  1, 0.5, 0#, 81/255, 179/255, 110/255
 ]
 
#цвет
#rgb = [52/255,178/255, 152/255]
 
 
# Исходный код вершинного шейдера
vertex_src = """
#version 440
 
layout(location=0) in vec3 vertex_position;     // координаты вершин
layout(location=1) in vec3 in_color;            // цвета вершин
out vec3 out_color;
 
void main()
{
    /* Передаем координаты на следующие
     * этапы графического конвейера
     * без матричных преобразований.
     */
     gl_Position = vec4(vertex_position, 1);
 
     // Передаем цвета во фрагментный шейдер.
     out_color = in_color;
}
"""
 
# Исходный код фрагментного шейдера
fragment_src = """
#version 440
 
//принимаем цвета из вершинного шейдера
in vec3 out_color;
 
out vec4 fragment_color;
 
void main()
{
    fragment_color = vec4(out_color, 1);
}
"""
 
#создание дисплея
def display_capXY():
 pg.caption = "Pygame with OpenGL"
 screen_width = 800
 screen_height = 600
 
 screen = pg.display.set_mode((screen_width, screen_height), pg.DOUBLEBUF | pg.OPENGL)
 
 pg.display.set_caption(pg.caption) 
 
 
#описание функции
'''shaders(координаты, цвет точек(r,g,b), надо-ли менять цвет какой-либо точки (True/False), если True: список индекса точек и ргб цвета'''
 
#создание шейдера в виде функции
def shaders(koordinaty, rgb, True_False, vertex_index):
 #создаем новый список
 koordinaty2 = []
 
 #цикл по набору данных в новый список koordinaty2 из двух других списков: koordinaty, rgb
 #из koordinaty достаем координаты, а из rgb значение цвета
 for i in range(len(koordinaty)):
   koordinaty2.append(koordinaty[i])
   if i%3==2:
    koordinaty2.append(rgb[0]);koordinaty2.append(rgb[1]);koordinaty2.append(rgb[2])
    
 #замена единичных цветов точек по их индексу
 if True_False:
   for i in range(len(vertex_index)):
    '''ЗДЕСЬ ОШИБКА'''
    index_vertex_rgb = (vertex_index[i][0] + 1.0) * 6 - 1.0
    koordinaty2[index_vertex_rgb-2]= vertex_index[i][3]
    koordinaty2[index_vertex_rgb-1]= vertex_index[i][2]
    koordinaty2[index_vertex_rgb]= vertex_index[i][1]
    
   
 
 #меняем переменную списка
 koordinaty = koordinaty2
 
 #превращаем список в байтовый файл
 koordinaty=np.array(koordinaty, dtype=np.float32)
 '''БУФЕР'''
 #создаем буфер
 buffer = glGenBuffers(1)
 
 #Привязываем буфер, чтобы затем загрузить в него данные
 glBindBuffer(GL_ARRAY_BUFFER, buffer) 
 
 #Передаем в буфер данные из массива
 #                       размер буфера в байтах    данные, которые хотим передать в буфер(наш список)
 #                                  \/                \/
 glBufferData(GL_ARRAY_BUFFER, koordinaty.nbytes, koordinaty, GL_STATIC_DRAW)
 
 '''ШЕЙДЕРЫ'''
 #Компилируем вершинный шейдер.
 vertex_shader = compileShader(vertex_src, GL_VERTEX_SHADER)
 
 #Компилируем фрагментный шейдер.
 fragment_shader = compileShader(fragment_src, GL_FRAGMENT_SHADER)
 
 #Объединяем шейдеры в шейдерную программу
 shader_program = compileProgram(vertex_shader, fragment_shader)
 
 #Делаем нашу шейдерную программу активной
 glUseProgram(shader_program)
 
 '''ПЕРЕДАЧА ДАННЫХ В ВЕРШИННЫЙ ШЕЙДЕР'''
 # Передаем координаты вершин в вершинный шейдер
 glEnableVertexAttribArray(0)
 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, koordinaty.itemsize*6, ctypes.c_void_p(0))
 
 #Передаем цвета вершин в вершинный шейдер
 glEnableVertexAttribArray(1)
 glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, koordinaty.itemsize*6, ctypes.c_void_p(koordinaty.itemsize*3))
 #функция отрисовки
 glDrawArrays(GL_TRIANGLES, 0, 3) 
 
 
#объединяем все функции в одну
def main(): 
  while True:
   for event in pg.event.get():
     if event.type == pg.QUIT:
       pg.quit()
       quit()
   glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
   
   #задаем фоновый цвет
   glClearColor(200/255, 200/255, 200/255, 1)
   
   #функция шейдеров
   shaders(koordinaty,(52/255,178/255, 152/255), True, ((0, 1, 1, 1)))
   
   #другие функции
   
   #прочее
   pg.display.flip() 
   
   
if __name__ == "__main__":
 #создаем дисплей
 display_capXY()
 #запукаем все функции
 main()
0
9991 / 2963 / 497
Регистрация: 05.10.2013
Сообщений: 8,135
Записей в блоге: 243
20.08.2024, 21:02
Daniil_G, надо было создать новую тему. Я создал и ответил в ней: TypeError: 'int' object is not subscriptable
0
9991 / 2963 / 497
Регистрация: 05.10.2013
Сообщений: 8,135
Записей в блоге: 243
21.08.2024, 13:50
Цитата Сообщение от 8Observer8 Посмотреть сообщение
Имея две точки начала и конца отрезка можно рассчитать середину отрезка, угол поворота и длину отрезка. Ширину отрезка мы задаём сами. Эти данные я использую, чтобы трансформировать квадрат для получения отрезка. Самое сложное - рассчитать угол имея две точки. Подумайте, как найти этот угол. Подсказка - тангенс угла - это отношения противолежащего катета с прилежащему. Отсюда угол. Напишите на Python, как найти арктангенс, то есть имея тангенс найти угол.
Имея два вектора: начало отрезка и конец можно легко рассчитать вектор самого отрезка отняв вектор конца от вектора начала, а чтобы найти середину вектора отрезка, а значит и координаты середины отрезка нужно к вектору начала прибавить вектор отрезка разделённого на 2. Это лучше всего нарисовать на бумаге в клеточку, чтобы лучше понять:

Python
1
2
3
4
def draw(self, start, end, color, thickness):
    # Находим центр отрезка
    v = end - start
    centerPosition = start + v / 2
Для расчёта угла наклона отрезка я использую кватернион вместо арктангенса, потому что это короче, а самое главное, это универсально - работает в 3D, так же как и в 2D. Кватернион - это другой способ задания поворота. Самому ничего считать не нужно, всё уже готово к использованию. C арктангенсом будет сложно в 3D - надо будет рассчитывать углы поворота вокруг трёх осей по проекциям на координатные плоскости. Для расчёта кватерниона нужны два вектора. Один - это вектор отрезка, который рассчитали выше (вектор "v"), а другой - это единичный вектор направленный вправо - от него считаем угол. Правда, в GLM я не нашёл такого метода, который бы рассчитывал кватернион между двумя векторами. Такой метод есть в Qt - rotationTo:

C++ (Qt)
1
QQuaternion QQuaternion::rotationTo(const QVector3D &from, const QVector3D &to)
Соответственно, этот метод доступен из PyQt и PySide. Точно такой же метод есть в glMatrix - это библиотека для JavaScript и TypeScript для работы с WebGL - rotationTo. Я просто переправил rotationTo из JavaScript на Python и поместил этот метод в класс MathHelper, а класс MathHelper поместил в отдельный файл math_helper.py. Рисование линии убрал в класс LinerDrawer, а класс LinerDrawer поместил в отдельный файл line_drawer.py Написал комментарии на русском и прикрепил архив с примером.



main.py

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# pip install Pygame PyOpenGL PyGLM numpy
# python main.py
 
import ctypes
 
import glm
import numpy as np
import pygame
from OpenGL.GL import *
from OpenGL.GL.shaders import *
from pygame.locals import *
 
from line_drawer import LineDrawer
 
vertexShaderSource = """
    attribute vec2 aPosition;
    uniform mat4 uMvpMatrix;
 
    void main()
    {
        gl_Position = uMvpMatrix * vec4(aPosition, 0.0, 1.0);
    }
"""
 
fragmentShaderSource = """
    uniform vec3 uColor;
 
    void main()
    {
        gl_FragColor = vec4(uColor, 1.0);
    }
"""
 
def main():
    pygame.init()
    winWidth, winHeight = 400, 400
    display = (winWidth, winHeight)
 
    pygame.display.set_mode(display, DOUBLEBUF | OPENGL)
 
    # Выставляем тёмно-серый цвет для очистки экрана
    glClearColor(0.2, 0.2, 0.2, 1)
 
    # Создаём объект program для взаимодействия с шейдерами
    program = compileProgram(
       compileShader(vertexShaderSource, GL_VERTEX_SHADER),
       compileShader(fragmentShaderSource, GL_FRAGMENT_SHADER))
 
    # Создаём матрицу ортогональной проекции и вида
    projMatrix = glm.ortho(-100, 100, -100, 100, 10, -10)
    viewMatrix = glm.lookAt(
        glm.vec3(0, 0, 10), # Позиция камеры
        glm.vec3(0, 0, 0), # Точку наблюдения
        glm.vec3(0, 1, 0)) # Направление макушки камеры
    # Комбинируем матрицу проекции и матрицу вида в одну матрицу
    projViewMatrix = projMatrix * viewMatrix
 
    # Создаём объект для рисований отрезков
    lineDrawer = LineDrawer(program, projViewMatrix)
 
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()
 
        # Очищаем экран заданным цветом
        glClear(GL_COLOR_BUFFER_BIT)
 
        # Рисуем отрезок красного цвета
        lineDrawer.draw(start = glm.vec3(-60, 50, 0), end = glm.vec3(40, 80, 0),
            color = glm.vec3(1, 0.5, 0.5), thickness = 1)
 
        # Рисуем отрезок синего цвета
        lineDrawer.draw(start = glm.vec3(-28, 37, 0), end = glm.vec3(80, 25, 0),
            color = glm.vec3(0.5, 0.5, 1), thickness = 3)
 
        # Рисуем ломанную зелёного цвета
        color = glm.vec3(0.5, 1, 0.5)
        thickness = 5
        lineDrawer.draw(start = glm.vec3(-81, -73, 0), end = glm.vec3(-43, -20, 0),
            color = color, thickness = thickness)
        lineDrawer.draw(start = glm.vec3(-43, -20, 0), end = glm.vec3(0, -5, 0),
            color = color, thickness = thickness)
        lineDrawer.draw(start = glm.vec3(0, -5, 0), end = glm.vec3(25, -10, 0),
            color = color, thickness = thickness)
        lineDrawer.draw(start = glm.vec3(25, -10, 0), end = glm.vec3(52, -70, 0),
            color = color, thickness = thickness)
        lineDrawer.draw(start = glm.vec3(52, -70, 0), end = glm.vec3(77, -5, 0),
            color = color, thickness = thickness)
 
        # Меняем местами задний и перений буферы
        pygame.display.flip()
        # Делаем задержку, чтобы ограничить FPS
        pygame.time.wait(10)
 
if __name__ == "__main__":
    main()
line_drawer.py

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import math
 
import glm
import numpy as np
from OpenGL.GL import *
 
from math_helper import MathHelper
 
 
class LineDrawer:
 
    def __init__(self, program, projViewMatrix):
        # Сохраняем ссылки на программу и projView-матрицу
        self.program = program
        self.projViewMatrix = projViewMatrix
 
        # Активируем принятую программу, чтобы получить доступ к
        # переменным шейдера
        glUseProgram(self.program)
 
        # Получаем доступ к переменным шейдера
        self.aPositionLocation = glGetAttribLocation(self.program, "aPosition")
        self.uColorLocation = glGetUniformLocation(self.program, "uColor")
        self.uMvpMatrixLocation = glGetUniformLocation(self.program, "uMvpMatrix")
 
        # Создание буфера в оперативной памяти видеокарты
        self.vertPosBuffer = glGenBuffers(1)
        self.initVertexBuffers()
 
    def initVertexBuffers(self):
        # Задаём вершины квадрата
        vertPositions = np.array([
            -0.5, -0.5,
            0.5, -0.5,
            -0.5, 0.5,
            0.5, 0.5
        ], dtype=np.float32)
        # Привязка к созданному буферу
        glBindBuffer(GL_ARRAY_BUFFER, self.vertPosBuffer)
        # Копирование массива вершин треугольника в буфер
        glBufferData(GL_ARRAY_BUFFER, len(vertPositions) * 4,
            vertPositions, GL_STATIC_DRAW)
 
    # Этот метод нужно вызывать в том случае, если изменяются
    # размеры окна или меняется положение камеры
    def setProjViewMatrix(self, projViewMatrix):
        self.projViewMatrix = projViewMatrix
 
    def bind(self):
        # Выбираем объект program
        glUseProgram(self.program)
 
        # Привязка к буферу
        glBindBuffer(GL_ARRAY_BUFFER, self.vertPosBuffer)
 
        # Настройка буфера
        glVertexAttribPointer(self.aPositionLocation, 2, GL_FLOAT, GL_FALSE,
            0, ctypes.c_void_p(0))
        glEnableVertexAttribArray(self.aPositionLocation)
 
    def draw(self, start, end, color, thickness):
        # Находим центр отрезка
        v = end - start
        centerPosition = start + v / 2
 
        # Длина отрезка
        length = glm.length(v)
 
        # Нормализованный вектор отрезка
        norm = glm.normalize(v);
 
        # Расчитываем угол отрезка
        rotation = MathHelper.rotationTo(glm.vec3(1, 0, 0), norm);
 
        # Создаём матрицу модели, то есть матрицу
        # объединяющую матрицу перемещения, матрицу поворота и scale-матрицу
        # Translate
        modelMatrix = glm.translate(glm.mat4(1), centerPosition)
        # Rotate
        rotationMatrix = glm.mat4_cast(rotation)
        modelMatrix = modelMatrix * rotationMatrix
        # Scale
        modelMatrix = glm.scale(modelMatrix, glm.vec3(length, thickness, 1))
 
        # Комбинируем projView-матрицу и матрицу модели в одну MVP-матрицу
        mvpMatrix = self.projViewMatrix * modelMatrix
 
        self.bind()
 
        # Отправляем MVP-матрицу в вершинный шейдер
        glUniformMatrix4fv(self.uMvpMatrixLocation, 1, GL_FALSE,
            glm.value_ptr(mvpMatrix))
 
        # Отправляем значение цвета в фрагментный шейдер
        glUniform3fv(self.uColorLocation, 1, glm.value_ptr(color))
 
        # Вызов команды рисования, которая спровоцирует
        # вызов вершинного шейдера 4 раза - по одному для
        # каждой вершины квадрата
        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)
math_helper.py

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import math
 
import glm
 
 
class MathHelper:
 
    # Sets a quat from the given angle and rotation axis, then returns it
    @staticmethod
    def setAxisAngle(axis, rad):
        rad = rad * 0.5
        s = math.sin(rad)
        out = glm.quat()
        out.x = s * axis[0]
        out.y = s * axis[1]
        out.z = s * axis[2]
        out.w = math.cos(rad)
        return out
 
    # This is port of https://glmatrix.net/docs/quat.js.html#line652
    # Sets a quaternion to represent the shortest rotation from one vector to another
    # Both vectors are assumed to be unit length
    @staticmethod
    def rotationTo(initialVector, destinationVector):
        xUnitVec3 = glm.vec3(1, 0, 0)
        yUnitVec3 = glm.vec3(0, 1, 0)
        out = glm.quat()
 
        dot = glm.dot(destinationVector, initialVector)
 
        if dot < -0.999999:
            tmpvec3 = glm.cross(initialVector, xUnitVec3)
            if glm.length(tmpvec3) < 0.000001:
                tmpvec3 = glm.cross(initialVector, yUnitVec3)
            tmpvec3 = glm.normalize(tmpvec3)
            out = MathHelper.setAxisAngle(tmpvec3, math.pi)
            return out
        elif dot > 0.999999:
            out.x = 0
            out.y = 0
            out.z = 0
            out.w = 1
            return out
        else:
            tmpvec3 = glm.cross(initialVector, destinationVector)
            out.x = tmpvec3[0]
            out.y = tmpvec3[1]
            out.z = tmpvec3[2]
            out.w = 1 + dot
            return glm.normalize(out)
Миниатюры
Вложения
Тип файла: zip line-drawer-opengl21-pygame-python.zip (4.1 Кб, 0 просмотров)
0
9991 / 2963 / 497
Регистрация: 05.10.2013
Сообщений: 8,135
Записей в блоге: 243
02.12.2024, 15:13
Я перевёл комментарии в примере сообщения #31 на английский с помощью Google Translate. Лучше всего писать комментарии на английском, потому что охват больше - можно общаться на англоязычных площадках, но не забывая, конечно, параллельно использовать КиберФорум. Есть много англоязычных мест, где очень много народу сидит и активность большая. Много русскоязычных, которые общаются на английском. Можно тренировать свой английский, исправляя себя в Google Translate перед публикацией текста. Это заставляет выходить из зоны комфорта и расти над собой.

Самая главная новость, что 29 ноября автор PyBox2D сделал возможным установку PyBox2D на Python 3.12+. Теперь вы можете поставить PyBox2D командой "pip install PyBox2D" с помощью Python 3.6 - 3.13 Ранее, PyBox2D работать только максимум с Python 3.8, а с 3.9 и далее уже не работал.

Сделать перемещение объекта в игре с помощью PyBox2D намного проще, чем с помощью написания свой системы определения коллизий. Когда прямоугольники повёрнуты, либо форма прямоугольная, то сделать это будет очень сложно. К тому же Box2D оптимизирован под большое количество объектов на сцене. Вам не надо будет самому писать код для прохода по всем объектам и проверять нет ли касания игрока с каким-либо объектом - Box2D сделает это за вас.

Для упрощения, пример написан на Pygame, но его несложно будет переписать на OpenGL, потому что Pygame здесь для окна, клавиш и рисования линий: https://gist.github.com/8Obser... 721429e16f

> pip install Box2D Pygame

main.py

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# pip install Box2D Pygame
 
import math
 
import pygame
from Box2D import (b2_dynamicBody, b2_kinematicBody, b2_staticBody, b2BodyDef,
                   b2ContactListener, b2FixtureDef, b2PolygonShape, b2Vec2,
                   b2World)
 
from debug_drawer import DebugDrawer
 
 
class ContactListener(b2ContactListener):
 
    def BeginContact(self, contact):
        userDataA = contact.fixtureA.userData
        userDataB = contact.fixtureB.userData
        print(userDataA["name"], "<--->", userDataB["name"])
 
def main():
 
    pygame.init()
 
    # Create a window of a given size with a title
    window = pygame.display.set_mode((268, 268))
    pygame.display.set_caption("PyBox2D and Pygame")
 
    # Create a physical world with zero gravity
    gravity=b2Vec2(0, 0)
    world = b2World(gravity)
 
    # The size of the physical world must be between 0.1 and 10
    # so a constant pixelsPerMeter is created to
    # translate from the pixel world to the Box2D physical world
    pixelsPerMeter = 30
 
    # Create DebugDrawer and configure it
    debugDrawer = DebugDrawer(window, pixelsPerMeter)
    debugDrawer.flags = { "drawShapes": True }
    world.renderer = debugDrawer
 
    # Create a ContactListener
    world.contactListener = ContactListener()
 
    # Creating a player body
    playerBodyDef = b2BodyDef() # Object for setting the position and type of the object
    # Set player position and type
    playerBodyDef.position = b2Vec2(200 / pixelsPerMeter, 100 / pixelsPerMeter)
    playerBodyDef.type = b2_dynamicBody
    # Prevent player from spinning
    playerBodyDef.fixedRotation = True
    # Create a player body
    playerBody = world.CreateBody(playerBodyDef)
    # The player must not fall asleep
    playerBody.sleepingAllowed = False
    # Create a player shape in the form of a rectangle
    playerShape = b2PolygonShape()
    playerShape.SetAsBox(10 / pixelsPerMeter, 20 / pixelsPerMeter)
    # Linking the player's body and form
    playerFixtureDef = b2FixtureDef()
    playerFixtureDef.shape = playerShape
    # Player density. This is analogous to mass
    playerFixtureDef.density = 1
    playerFixture = playerBody.CreateFixture(playerFixtureDef)
    playerFixture.userData = { "name": "player" }
 
    # Create a wall similar to the player, but now
    # the body type is static and density = 0
    wallBodyDef = b2BodyDef()
    # Set the position and type of the wall
    wallBodyDef.position = b2Vec2(100 / pixelsPerMeter, 150 / pixelsPerMeter)
    wallBodyDef.type = b2_staticBody
    wallBodyDef.angle = math.radians(30)
    # Create the body of the wall
    wallBody = world.CreateBody(wallBodyDef)
    # Create a player shape in the form of a rectangle
    wallShape = b2PolygonShape()
    wallShape.SetAsBox(10 / pixelsPerMeter, 50 / pixelsPerMeter)
    # Combine wall body and shape
    wallFixtureDef = b2FixtureDef()
    wallFixtureDef.shape = wallShape
    # Wall density. This is analogous to mass.
    wallFixtureDef.density = 0
    wallFixture = wallBody.CreateFixture(wallFixtureDef)
    wallFixture.userData = { "name": "wall" }
 
    # Create a kinematic fan
    # with a static body and density = 0
    fanBodyDef = b2BodyDef()
    # Set the position and type of the fan
    fanBodyDef.position = b2Vec2(200 / pixelsPerMeter, 200 / pixelsPerMeter)
    fanBodyDef.type = b2_kinematicBody
    fanBodyDef.angle = math.radians(0)
    # Create the body of the fan
    fanBody = world.CreateBody(fanBodyDef)
    # Create a rectangular fan shape
    fanShape = b2PolygonShape()
    fanShape.SetAsBox(5 / pixelsPerMeter, 30 / pixelsPerMeter)
    # Connect the fan body and the shape
    fanFixtureDef = b2FixtureDef()
    fanFixtureDef.shape = fanShape
    # Fan density. This is analogous to mass.
    fanFixtureDef.density = 0
    fanFixture = fanBody.CreateFixture(fanFixtureDef)
    fanFixture.userData = { "name": "fan" }
 
    # Dictionary for storing pressed keys
    keys = { "left": False, "right": False, "up": False, "down": False }
 
    clock = pygame.time.Clock()
    playerSpeed = 5
    fanSpeed = 180
 
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()
            if event.type == pygame.KEYDOWN:
                key = pygame.key.name(event.key)
                keys[key] = True
            if event.type == pygame.KEYUP:
                key = pygame.key.name(event.key)
                keys[key] = False
 
        if keys["left"]:
            playerBody.linearVelocity.x = -playerSpeed
        if keys["right"]:
            playerBody.linearVelocity.x = playerSpeed
        if keys["up"]:
            playerBody.linearVelocity.y = -playerSpeed
        if keys["down"]:
            playerBody.linearVelocity.y = playerSpeed
 
        # Set the number of frames per second (60) and
        # calculate the time interval between frames dt (~0.16)
        dt = clock.tick(60) / 1000
 
        # Set the angle of the fan
        fanBody.angle += math.radians(fanSpeed * dt)
 
        # Take physics simulation one step further
        world.Step(dt, 3, 2)
 
        # Stop the player
        playerBody.linearVelocity = b2Vec2(0, 0)
 
        # Fill the screen with black color
        window.fill(0)
 
        # Drawing colliders
        world.DrawDebugData()
 
        # Updating the display
        pygame.display.flip()
 
if __name__ == '__main__':
    main()
debug_drawer.py

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import math
 
import pygame
from Box2D import b2Draw
 
 
class DebugDrawer(b2Draw):
 
    def __init__(self, window, pixelsPerMeter, thickness=3):
        super().__init__()
        self.window = window
        self.pixelsPerMeter = pixelsPerMeter
        self.thickness = thickness
 
    def DrawSolidPolygon(self, vertices, color):
        pygame.draw.line(self.window, (color.r * 255, color.g * 255, color.b * 255),
            (vertices[0][0] * self.pixelsPerMeter, vertices[0][1] * self.pixelsPerMeter),
            (vertices[1][0] * self.pixelsPerMeter, vertices[1][1] * self.pixelsPerMeter),
            self.thickness)
        pygame.draw.line(self.window, (color.r * 255, color.g * 255, color.b * 255),
            (vertices[1][0] * self.pixelsPerMeter, vertices[1][1] * self.pixelsPerMeter),
            (vertices[2][0] * self.pixelsPerMeter, vertices[2][1] * self.pixelsPerMeter),
            self.thickness)
        pygame.draw.line(self.window, (color.r * 255, color.g * 255, color.b * 255),
            (vertices[2][0] * self.pixelsPerMeter, vertices[2][1] * self.pixelsPerMeter),
            (vertices[3][0] * self.pixelsPerMeter, vertices[3][1] * self.pixelsPerMeter),
            self.thickness)
        pygame.draw.line(self.window, (color.r * 255, color.g * 255, color.b * 255),
            (vertices[3][0] * self.pixelsPerMeter, vertices[3][1] * self.pixelsPerMeter),
            (vertices[0][0] * self.pixelsPerMeter, vertices[0][1] * self.pixelsPerMeter),
            self.thickness)
 
    def DrawSolidCircle(self, center, radius, axis, color):
        pass
    def DrawPolygon(self, vertices, color):
        pass
    def DrawSegment(self, p1, p2, color):
        pass
    def DrawPoint(self, p, size, color):
        pass
    def DrawCircle(self, center, radius, color, drawwidth=1):
        pass
    def DrawTransform(self, xf):
        pass


Миниатюры
1
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
02.12.2024, 15:13

Нужно сделать переход или как анимацию кнопки
Хочу сделать анимацию кнопки. Не знаю какой компонент взять, можно ли на Timage, TBitBtn? К примеру как: Есть изображение кнопки, это и...

Можно ли в WF вставить готовую анимацию (или как сделать самому)?
Всем привет. Начал делать демонстрацию в WF для пирамидальной сортировки и возник такой вопрос,можно ли в WF вставить готовую анимацию...

Как сделать в Bash рандомное изменение координат курсора на экране каждые 5 минут?
Кто может набросать скриптик вот такой в баше? Добавлено через 6 минут Я понимаю что для этого наверное надо считывать какое...

Возможно ли сделать как-то проверку было ли изменение экрана или нет?
Подскажите возможно ли сделать как то проверку было ли изменение экрана или нет $(window).resize() то есть если загрузить только...

Сделать анимацию анимацию-движение картинка
Сделать анимацию анимацию-движение картинка, которая убегает от курсора мышки. Анимацию сделать с учетом законов механики (т.е. в начале...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
36
Ответ Создать тему
Новые блоги и статьи
Запустил конкурс "тем и промптов для текстовых квестов созданных почти чисто ИИ"
Adler 06.10.2026
Всем привет! За последние три-четыре дня я создал более 16 текстовых квестовых игр используя преимущественно по одному запросу к ИИ на игру. Мне так понравилось смотреть все ветки/ сцены во всех. . .
ИИ не может найти нужный язык в списке
Supersumestria 05.10.2026
Я ему даю вот такое изображение и прошу найти и подчеркнуть немецкий язык. Возвращает он вот это: https:/ / i. **********/ vqBWLe2. png Нужную строчку в 3й колонке просто выдумал. . Это. . .
Новая последняя моя музыка в SUNO
zorxor 05.10.2026
Здравствуйте, дорогие мои друзья! С большой радостью я хотел бы представить вам свою новую последнею музыку, которую сгенерировала мне по моей просьбе нейросеть SUNO. С уважением, zorxor. Это. . .
Nekobox - outbounds[0].transport: unknown transport type: raw
damix 01.10.2026
Фикс ошибки Правым кликом по серверу -> отладочная информация -> edit Заменить "net": "raw", на "net": "tcp", Нажать кнопку reload.
Программный домашний кинотеатр
russiannick 27.09.2026
Сподобился на программный домашний кинотеатр. В качестве ЯВУ по традиции выбрал js. В помощники взял Яндекс-Алису. Было создано три зала на разные интересы. исторические и ретро сериал Хичкок. . .
Беседа с ИИ о программистах, недопускающих к созданию и правке кода генеративные ИИ и причины этого
zorxor 21.09.2026
Раньше я радовался или получал некоторые эмоции, пусть небольшие, но всё же, от самого процесса написания кода, рекомпиляции и запуска, видя постепенное развитие программы и прочее. А теперь лень. . .
Мобильное приложение ColorStep
pavlinmavlin 17.09.2026
Реализовал приложение Красный, Зеленый, Синий в Unity3d + c#. Название изменил на ColorStep. Приложение прошло модерацию и теперь доступно для скачивания. Делал его сам, шаг за шагом — и вот,. . .
Запрет дублирования строк в табличной части
Maks 13.09.2026
Реализация из решения ниже выполнена на нетиповом справочнике "Нормы ТО" с табличной часть "Виды ТО", разработанного в КА2, со следующими реквизитами: - ВидТО (СправочникСсылка. ВидыТО); - ВидГСМ. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru