3

我正在使用 PyOpenGL 制作游戏。我想每 4 秒从一个地方发射一次子弹,但是当我用来gluSphere()绘制子弹时,它并没有出现在我例外的地方(除了第一次绘制球体)。我该如何解决?

相关的代码:

import threading
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *

class Object:
    def __init__(self):
        self.bullet = []

def draw_bullet(pos):
    glTranslated(pos[0], pos[1], pos[2]) #Move to the place
    glColor4f(1, 1, 1, 1) #Put color
    gluSphere(sphere, 0.5, 32, 16) #Draw sphere

def shoot(obj):
    global shoottimer
    shoottimer = None
    obj.bullet.append(pos)

object = Object()
shoottimer = None
pos = [0,0,0]
while True:
    if shoottimer == None:
        shoottimer = threading.Timer(4,shoot,args = [object])
        shoottimer.start()
    for bullet in person.bullet:
        person.draw_bullet(bullet)

完整代码:

#!/usr/local/bin/python3
import pygame
from pygame.locals import *

from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *

import math,sys,numpy,random,ctypes,threading,os

pygame.init()
display = (1440, 900)
screen = pygame.display.set_mode(display, DOUBLEBUF | OPENGL | RESIZABLE)

glEnable(GL_DEPTH_TEST)
glEnable(GL_LIGHTING)
glShadeModel(GL_SMOOTH)
glEnable(GL_COLOR_MATERIAL)
glEnable(GL_BLEND)
glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE)

glEnable(GL_LIGHT0)
glLightfv(GL_LIGHT0, GL_AMBIENT, [0.5, 0.5, 0.5, 1])
glLightfv(GL_LIGHT0, GL_DIFFUSE, [1.0, 1.0, 1.0, 1])

glMatrixMode(GL_PROJECTION)
gluPerspective(45, (display[0]/display[1]), 0.1, 50.0)

glMatrixMode(GL_MODELVIEW)
gluLookAt(0, -8, 0, 0, 0, 0, 0, 0, 1)
glTranslatef(0,-8,0)
viewMatrix = glGetFloatv(GL_MODELVIEW_MATRIX)
glLoadIdentity()

# init mouse movement and center mouse on screen
displayCenter = [screen.get_size()[i] // 2 for i in range(2)]
mouseMove = [0, 0]
pygame.mouse.set_pos(displayCenter)

cmddown = False
light = 0
mouseclick = False
cam_attack = False
cam_damage = random.randint(20,30)
max_health = 100
health = max_health
knockback = False
knocktimer = None
shoottimer = None
sphere = gluNewQuadric()
dist = None
dist_list = []
person_count = 1
up_down_angle = 0.0
camera_pos = (0,0,0)
paused = False
run = True
#xzy = xyz

#Functions & Classes
def InverseMat44(mat):
    m = [mat[i][j] for i in range(4) for j in range(4)]
    inv = [0]*16

    inv[0]  =  m[5] * m[10] * m[15] - m[5] * m[11] * m[14] - m[9] * m[6] * m[15] + m[9] * m[7] * m[14] + m[13] * m[6] * m[11] - m[13] * m[7] * m[10]
    inv[4]  = -m[4] * m[10] * m[15] + m[4] * m[11] * m[14] + m[8] * m[6] * m[15] - m[8] * m[7] * m[14] - m[12] * m[6] * m[11] + m[12] * m[7] * m[10]
    inv[8]  =  m[4] * m[9]  * m[15] - m[4] * m[11] * m[13] - m[8] * m[5] * m[15] + m[8] * m[7] * m[13] + m[12] * m[5] * m[11] - m[12] * m[7] * m[9]
    inv[12] = -m[4] * m[9]  * m[14] + m[4] * m[10] * m[13] + m[8] * m[5] * m[14] - m[8] * m[6] * m[13] - m[12] * m[5] * m[10] + m[12] * m[6] * m[9]
    inv[1]  = -m[1] * m[10] * m[15] + m[1] * m[11] * m[14] + m[9] * m[2] * m[15] - m[9] * m[3] * m[14] - m[13] * m[2] * m[11] + m[13] * m[3] * m[10]
    inv[5]  =  m[0] * m[10] * m[15] - m[0] * m[11] * m[14] - m[8] * m[2] * m[15] + m[8] * m[3] * m[14] + m[12] * m[2] * m[11] - m[12] * m[3] * m[10]
    inv[9]  = -m[0] * m[9]  * m[15] + m[0] * m[11] * m[13] + m[8] * m[1] * m[15] - m[8] * m[3] * m[13] - m[12] * m[1] * m[11] + m[12] * m[3] * m[9]
    inv[13] =  m[0] * m[9]  * m[14] - m[0] * m[10] * m[13] - m[8] * m[1] * m[14] + m[8] * m[2] * m[13] + m[12] * m[1] * m[10] - m[12] * m[2] * m[9]
    inv[2]  =  m[1] * m[6]  * m[15] - m[1] * m[7]  * m[14] - m[5] * m[2] * m[15] + m[5] * m[3] * m[14] + m[13] * m[2] * m[7]  - m[13] * m[3] * m[6]
    inv[6]  = -m[0] * m[6]  * m[15] + m[0] * m[7]  * m[14] + m[4] * m[2] * m[15] - m[4] * m[3] * m[14] - m[12] * m[2] * m[7]  + m[12] * m[3] * m[6]
    inv[10] =  m[0] * m[5]  * m[15] - m[0] * m[7]  * m[13] - m[4] * m[1] * m[15] + m[4] * m[3] * m[13] + m[12] * m[1] * m[7]  - m[12] * m[3] * m[5]
    inv[14] = -m[0] * m[5]  * m[14] + m[0] * m[6]  * m[13] + m[4] * m[1] * m[14] - m[4] * m[2] * m[13] - m[12] * m[1] * m[6]  + m[12] * m[2] * m[5]
    inv[3]  = -m[1] * m[6]  * m[11] + m[1] * m[7]  * m[10] + m[5] * m[2] * m[11] - m[5] * m[3] * m[10] - m[9]  * m[2] * m[7]  + m[9]  * m[3] * m[6]
    inv[7]  =  m[0] * m[6]  * m[11] - m[0] * m[7]  * m[10] - m[4] * m[2] * m[11] + m[4] * m[3] * m[10] + m[8]  * m[2] * m[7]  - m[8]  * m[3] * m[6]
    inv[11] = -m[0] * m[5]  * m[11] + m[0] * m[7]  * m[9]  + m[4] * m[1] * m[11] - m[4] * m[3] * m[9]  - m[8]  * m[1] * m[7]  + m[8]  * m[3] * m[5]
    inv[15] =  m[0] * m[5]  * m[10] - m[0] * m[6]  * m[9]  - m[4] * m[1] * m[10] + m[4] * m[2] * m[9]  + m[8]  * m[1] * m[6]  - m[8]  * m[2] * m[5]

    det = m[0] * inv[0] + m[1] * inv[4] + m[2] * inv[8] + m[3] * inv[12]
    for i in range(16):
        inv[i] /= det
    return inv

def clicked(tar_x,tar_y,tarimg):
    if tar_x < cursor_x < tar_x + tarimg.get_width() and tar_y < cursor_y < tar_y + tarimg.get_height():
        return True
    return False

def follower(x,y,z,x1,y1,z1,speed):
    dir_x, dir_y = (x1-x, y1-y)
    distance = math.hypot(dir_x, dir_y)
    dir_x, dir_y = (dir_x/distance, dir_y/distance)
    angle = math.degrees(math.atan2(dir_y, dir_x)) + 90
    return (dir_x*speed, dir_y*speed, 0, angle)

def random_pos(max_distance):
    x_value_change = random.randrange(-max_distance + 2,max_distance + 2)
    y_value_change = random.randrange(-max_distance + 20,max_distance + 3)
    z_value_change = 0
    return (x_value_change, y_value_change, z_value_change)

def blit_text(x,y,font,text,r,g,b):
    glColor3f(r,g,b)
    glWindowPos2f(x,y)
    for ch in text:
        glutBitmapCharacter(font,ctypes.c_int(ord(ch)))

def stopknock() :
    global knockback,knocktimer,health
    knockback = False
    knocktimer = None
    health -= random.randint(20,40)

def shoot(obj):
    global shoottimer
    shoottimer = None
    obj.bullet.append(obj.pos)

def subtract(v0, v1):
    return [v0[0]-v1[0], v0[1]-v1[1], v0[2]-v1[2]]
def dot(v0, v1):
    return v0[0]*v1[0]+v0[1]*v1[1]+v0[2]*v1[2]
def length(v):
    return math.sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2])
def mults(v, s):
    return [v[0]*s, v[1]*s, v[2]*s]
def add(v0, v1):
    return [v0[0]+v1[0], v0[1]+v1[1], v0[2]+v1[2]]
def cross(v0, v1):
    return [
        v0[1]*v1[2]-v1[1]*v0[2],
        v0[2]*v1[0]-v1[2]*v0[0],
        v0[0]*v1[1]-v1[0]*v0[1]]
def normalize(v):
    l = length(v)
    return [v[0]/l, v[1]/l, v[2]/l]
def PointInOrOn( P1, P2, A, B ):
    CP1 = cross(subtract(B, A), subtract(P1, A))
    CP2 = cross(subtract(B, A), subtract(P2, A))
    return dot( CP1, CP2 ) >= 0
def PointInOrOnTriangle( P, A, B, C ):
    return PointInOrOn(P, A, B, C) and PointInOrOn(P, B, C, A) and PointInOrOn(P, C, A, B)

def isectPlane(p0, p1, PA, PB, PC):
    R0 = p0      # origin 
    D = normalize(subtract(p1, p0))
    P0 = PA
    NV = normalize(cross(subtract(PB, PA), subtract(PC, PA)))
    dist_isect = dot(subtract(P0, R0), NV) / dot(D, NV) 
    P_isect = add(R0, mults(D, dist_isect))
    return P_isect, dist_isect

def isectQuad(p0, p1, PA, PB, PC, PD):
    P, t = isectPlane(p0, p1, PA, PB, PC)
    if t >= 0 and (PointInOrOnTriangle(P, PA, PB, 
        PC) or PointInOrOnTriangle(P, PA, PC, PD)):
        return t
    return None

def isectCuboid(p0, p1, pMin, pMax):
    t = None
    try:
        pl = [ [pMin[0], pMin[1], pMin[2]], [pMax[0], pMin[1], pMin[2]], [pMax[0], pMax[1], pMin[2]], [pMin[0], pMax[1], pMin[2]],
               [pMin[0], pMin[1], pMax[2]], [pMax[0], pMin[1], pMax[2]], [pMax[0], pMax[1], pMax[2]], [pMin[0], pMax[1], pMax[2]] ]
        il = [[0, 1, 2, 3], [4, 5, 6, 7], [4, 0, 3, 7], [1, 5, 6, 2], [4, 3, 1, 0], [3, 2, 6, 7]]
        for qi in il:
            ts = isectQuad(p0, p1, pl[qi[0]], pl[qi[1]], pl[qi[2]], pl[qi[3]])
            if ts != None and ts >= 0 and (t == None or ts < t):
                t = ts
    except:
        t = None
    return t

class Ground:
    def __init__(self,mul=1):
        self.vertices = [
        [-20,20,-1],
        [20,20,-1],
        [-20,-300,-1],
        [20,-300,-1]
        ]

    def draw(self):
        glBegin(GL_QUADS) #Begin fill
        for vertex in self.vertices:
            glColor3f(0,0.5,0.5)
            glVertex3fv(vertex)
        glEnd()

class Person:
    def __init__(self):
        self.vertices = [
            [-1,0,1],
            [-1,0,-1],
            [1,0,-1],
            [1,0,1],
            [-1,1,1],
            [-1,1,-1],
            [1,1,-1],
            [1,1,1]
        ]

        self.vertices = list(numpy.multiply(numpy.array(self.vertices),1))
        self.edges = (
            (0,1),
            (0,3),
            (0,4),
            (1,2),
            (1,5),
            (2,3),
            (2,6),
            (3,7),
            (4,5),
            (4,7),
            (5,6),
            (6,7)
            )
        self.surfaces = (
            (0,1,2,3),
            (0,1,5,4),
            (4,5,6,7),
            (1,2,6,5),
            (0,3,7,4),
            (2,3,7,6)
            )
        self.x = self.vertices[1][0]
        self.y = self.vertices[1][2]
        self.z = self.vertices[1][1]
        self.pos = (self.x,self.y,self.z)
        self.rot = 0
        self.health = 100
        self.damage = random.randint(20,40)
        self.bullet = []

    def draw(self):
        glTranslated(self.pos[0], self.pos[1], self.pos[2])
        glRotated(self.rot,0,0,1)

        #Get current view matrix, projection matrix and viewport rectangle 
        mv_matrix = glGetDoublev(GL_MODELVIEW_MATRIX)
        proj_matrix = glGetDoublev(GL_PROJECTION_MATRIX)
        vp_rect = glGetIntegerv(GL_VIEWPORT)

        #Calculate "near" and "far" point 
        pt_near = gluUnProject(displayCenter[0], displayCenter[1], 0, mv_matrix, proj_matrix, vp_rect)
        pt_far  = gluUnProject(displayCenter[0], displayCenter[1], 1, mv_matrix, proj_matrix, vp_rect)
        dist = isectCuboid(pt_near, pt_far, [-1, 0, -1], [1, 1, 1])

        glBegin(GL_QUADS) #Begin fill
        for surface in self.surfaces:
            for vertex in surface:
                glColor3f(0,1,0)
                glVertex3fv(self.vertices[vertex])
        glEnd()
        glLineWidth(5) #Set width of the line
        glBegin(GL_LINES) #Begin outline
        for edge in self.edges:
            for vertex in edge:
                glColor3f(1,1,0)
                glVertex3fv(self.vertices[vertex])
        glEnd()
        return dist

    def draw_bullet(self,pos): #This is the part where I got stuck
        glLoadIdentity()
        glTranslated(pos[0], pos[1], pos[2]) #Move to the place
        glColor4f(1, 1, 1, 1) #Put color
        gluSphere(sphere, 0.5, 32, 16) #Draw sphere

    def move(self,x,y,z):
        self.pos = (self.pos[0]+x,self.pos[1]+y,self.pos[2]+z)

glutInit()
persons = [Person() for person in range(person_count)]
ground = Ground()
for person in persons:
    person.pos = random_pos(12)
while run:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_LMETA:
                cmddown = True
            if event.key == pygame.K_ESCAPE:
                paused = not paused
            if event.key == pygame.K_q:
                if cmddown:
                    run = False
        if not paused:
            if event.type == pygame.MOUSEMOTION:
                mouseMove = [event.pos[i] - displayCenter[i] for i in range(2)]
                pygame.mouse.set_pos(displayCenter)
            if event.type == pygame.MOUSEBUTTONDOWN:
                if event.button == 1:
                    cam_attack = True
        else:
            if event.type == pygame.MOUSEBUTTONDOWN:
                if event.button == 1:
                    mouseclick = True
        if event.type == pygame.KEYUP:
            if event.key == pygame.K_LMETA:
                cmddown = False

    pygame.mouse.set_visible(False)
    if not paused:
        pygame.mouse.set_pos(displayCenter)
        #Get keys
        keypress = pygame.key.get_pressed()

        #Init model view matrix
        glLoadIdentity()

        #------------------------View------------------------
        #Apply the look up and down (with 90° angle limit)
        if up_down_angle < -90:
            if mouseMove[1] > 0:
                up_down_angle += mouseMove[1]*0.1
        elif up_down_angle > 90:
            if mouseMove[1] < 0:
                up_down_angle += mouseMove[1]*0.1
        else:
            up_down_angle += mouseMove[1]*0.1
        glRotatef(up_down_angle, 1.0, 0.0, 0.0)

        #Init the view matrix
        glPushMatrix()
        glLoadIdentity()

        #Apply the movement 
        if keypress[pygame.K_w]:
            glTranslatef(0,0,0.1)
        if keypress[pygame.K_s]:
            glTranslatef(0,0,-0.1)
        if keypress[pygame.K_d]:
            glTranslatef(-0.1,0,0)
        if keypress[pygame.K_a]:
            glTranslatef(0.1,0,0)

        #Apply the look left and right
        glRotatef(mouseMove[0]*0.1, 0.0, 1.0, 0.0)
        #------------------------View------------------------

        #Multiply the current matrix by the new view matrix and store the final view matrix 
        glMultMatrixf(viewMatrix)
        #Knockback
        if knockback:
            if knocktimer == None:
                knockback_dist = 0.4
                knockback_dir = normalize(subtract(knockback, camera_pos))
            glTranslatef(knockback_dir[0]*knockback_dist, knockback_dir[1]*knockback_dist, 0)
            if knocktimer == None:
                knocktimer = threading.Timer(0.2,stopknock)
                knocktimer.start()
        #Bullet
        for person in persons:
            if shoottimer == None:
                shoottimer = threading.Timer(4,shoot,args = [person])
                shoottimer.start()
        viewMatrix = glGetFloatv(GL_MODELVIEW_MATRIX)
        invVM = InverseMat44(viewMatrix)
        camera_pos = (invVM[12],invVM[13],invVM[14])

        #Apply view matrix
        glPopMatrix()
        glMultMatrixf(viewMatrix)

        glLightfv(GL_LIGHT0, GL_POSITION, [1, -1, 1, 0])

        #Follow, attack
        crosshair_color = (1,1,1)
        attacklist = []
        for counter,person in enumerate(persons):
            freturn = follower(person.pos[0],person.pos[1],person.pos[2],camera_pos[0],camera_pos[1],camera_pos[2],0.02)
            xchange,ychange,zchange = freturn[0],freturn[1],freturn[2]
            person.rot = freturn[3]
            if (touched(person.pos[0],person.pos[1],person.pos[2],camera_pos[0],camera_pos[1],camera_pos[2])) < 3:
                xchange,ychange,zchange = 0,0,0
                knockback = person.pos
            person.move(xchange,ychange,zchange)
            if (touched(person.pos[0],person.pos[1],person.pos[2],camera_pos[0],camera_pos[1],camera_pos[2])) < 5 and distlist[counter] != None:
                crosshair_color = (1,0,0)
                if len(attacklist) >= 2:
                    bigger = attacklist[1] > touched(person.pos[0],person.pos[1],person.pos[2],camera_pos[0],camera_pos[1],camera_pos[2])
                    if bigger == True:
                        attacklist = [person,touched(person.pos[0],person.pos[1],person.pos[2],camera_pos[0],camera_pos[1],camera_pos[2])]
                else:
                    attacklist = [person,touched(person.pos[0],person.pos[1],person.pos[2],camera_pos[0],camera_pos[1],camera_pos[2])]
        distlist = []
        if attacklist:
            if cam_attack == True:
                attacklist[0].health -= cam_damage

        glClearColor(0.53 + light, 0.63 + light, 0.98 + light, 1) #Change background colour (sky blue)
        glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)

        blit_text(displayCenter[0] - 700,displayCenter[1] + 410,GLUT_BITMAP_TIMES_ROMAN_24,"Health: " + str(health) + "/" + str(max_health),0,0,0)

        #Draw crosshair, health 
        if health > 0:
            blit_text(displayCenter[0] - 5,displayCenter[1] - 5,GLUT_BITMAP_TIMES_ROMAN_24,"+",crosshair_color[0],crosshair_color[1],crosshair_color[2])

        glPushMatrix()

        glColor4f(0.2, 0.2, 0.5, 1)
        for person in persons:
            glPushMatrix()
            distlist.append(person.draw())
            for bullet in person.bullet:
                person.draw_bullet(bullet)
            glPopMatrix()

        ground.draw()
        glPopMatrix()

        for person in persons:
            if person.health <= 0:
                persons.remove(person)

        if not persons and health > 0:
            pass #Show victory

        if health <= 0:
            light -= 0.04
            glLightfv(GL_LIGHT0, GL_AMBIENT, [0.5 + light, 0.5 + light, 0.5 + light, 1])
            glLightfv(GL_LIGHT0, GL_DIFFUSE, [1.0 + light, 1.0 + light, 1.0 + light, 1])

        cam_attack = False

    else:
        cursor_x,cursor_y = pygame.mouse.get_pos()
        pygame.mouse.set_visible(True)
        glClearColor(0.53 + light, 0.63 + light, 0.98 + light, 1) #Change background colour (sky blue)
        glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
        mouseclick = False
        for person in persons:
            glPushMatrix()
            distlist.append(person.draw())
            for bullet_shot in person.bullet:
                person.draw_bullet(bullet_shot)
            glPopMatrix()
        ground.draw()
    pygame.display.flip()
    pygame.time.wait(10)
pygame.quit()

我预计它会绘制球体,但它关闭了灯光并且什么也没做(可能在其他地方绘制)。

4

2 回答 2

2

每个对象都有其模型矩阵。模型矩阵分别由glTranslate应用glRotate
调用此函数会更改当前矩阵。为确保模型变换仅适用于 1 个模型,而不适用于之后绘制的所有模型,当前矩阵必须glPushMatrix在更改当前矩阵之前“保存”,并且必须glPopMatrix在绘制模型之后“恢复” .

绘制Person对象时执行此操作:

class Person:

    # [...]

    def draw(self):

        glPushMatrix() # <--- save

        glTranslated(self.pos[0], self.pos[1], self.pos[2])
        glRotated(self.rot,0,0,1)

        # [...]

        glPopMatrix()  # <--- restore
        return dist

当你画子弹时:

class Person:

    # [...]

    def draw_bullet(self,pos): #This is the part where I got stuck

        glPushMatrix() # <--- save

        glTranslated(pos[0], pos[1], pos[2]) #Move to the place
        glColor4f(1, 1, 1, 1) #Put color
        gluSphere(sphere, 0.5, 32, 16) #Draw sphere

        glPopMatrix()  # <--- restore

注意,视图矩阵设置在模型矩阵之前。定义的矩阵glTranslate乘以当前矩阵。这就是你想要的:

mdelview = view * model

因此,您必须在设置模型之前跳过设置单位矩阵:

def draw_bullet(self,pos): #This is the part where I got stuck

    glPushMatrix()

    # glLoadIdentity() <--- delete

    glTranslated(pos[0], pos[1], pos[2]) #Move to the place
    glColor4f(1, 1, 1, 1) #Put color
    gluSphere(sphere, 0.5, 32, 16) #Draw sphere
    glPopMatrix()

此外,我建议在新子弹产生时复制位置:

def shoot(obj):
    global shoottimer
    shoottimer = None

    # obj.bullet.append(obj.pos)
    obj.bullet.append(obj.pos[:])
于 2019-07-01T15:37:27.767 回答
2

glTranslated 向当前矩阵堆栈添加新的翻译。如果要指定新位置,则必须先撤消先前的翻译。

您应该能够通过glLoadIdentity()在 draw_bullet 方法的开头调用来解决您的问题。

现在在看到你的整个代码之后,更好的解决方案是将draw_bullet方法包装在 push/pop 块中:

def draw_bullet(self,pos): #This is the part where I got stuck
    glPushMatrix()
    glLoadIdentity()
    glTranslated(pos[0], pos[1], pos[2]) #Move to the place
    glColor4f(1, 1, 1, 1) #Put color
    gluSphere(sphere, 0.5, 32, 16) #Draw sphere
    glPopMatrix()

作为记录:您使用的 OpenGL 方法(固定功能管道)已经过时了十多年了。真的应该尽量避免使用 OpenGL 3.3 + Core Profile。

于 2019-07-01T09:13:52.110 回答