I am making a game using PyOpenGL. I want to have bullets shot from a place every 4 seconds, but when I used gluSphere()
to draw the bullet it doesn't appear to the place I excepted (except the first time of drawing the sphere). How can I solve it?
Code that is relevant:
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)
Full code:
#!/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()
I expected it to draw the spheres, but instead it turned off the lightings and did nothing (maybe drawn somewhere else).
Each object has its model matrix. The model matrix is applied by glTranslate
respctively glRotate
.
Calling this functions change the current matrix. To ensure that the model transformation is only applied to 1 model and not to all the models which are drawn after, the current matrix has to be "saved" by glPushMatrix
before changing the current matrix and has to be
"restored" by glPopMatrix
after drawing the model.
Do this when you draw the Person
object:
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
And when you draw the bullet:
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
Note, the view matrix is set before the model matrix. The matrix which define by glTranslate
is multiplied to the current matrix. That is what you want:
mdelview = view * model
So you've to skip setting the identity matrix before setting the 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()
Further I recommend to copy the position when a new bullet spawns:
def shoot(obj):
global shoottimer
shoottimer = None
# obj.bullet.append(obj.pos)
obj.bullet.append(obj.pos[:])