`
westice
  • 浏览: 114378 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

pyopengl网络游戏应用

阅读更多

 

     这个游戏很简单,可以操纵的飞机不断发子弹,前面不断有敌机来袭,子弹打中敌机然后敌机和子弹消失,最后实现了网络互联功能,允许两个人在局域网中不同机器上操作各自的飞机,平且两个游戏界面是同步的。

我这样设计的:自己维护一个飞机,子弹队列,敌机队列,将网络来的数据打包成另一个飞机,子弹队列,敌机队列,所以这里面传送的数据有自己飞机数据,子弹队列,敌机队列,这些数据通过TCP在服务器和客户端之间不断更新,传递。

     图形用opengl渲染,由于python不能直接访问指针,通过网络只能传递字符串型,所以传送二进制数据有点问题,自己写了对函数,用于字符串和二进制数的转换,每个数由长度为4的字符串组成,索引高的为数据高位,最高位为符号位,所以只能保存-1600万到1600万之间的数,对于视频游戏这个范围足矣,但有个缺陷,不知道怎么传浮点数,所以需要浮点数的地方先*1000,传送后除以1000得到浮点数。

     为了更像网络游戏,跟CS一样能自动搜索局域网中的服务器,实现了一个较简单实用的方法,大致是这样的:服务器启动后开放一个udp端口udpport,客户端向局域网中所有机器udpport发送验证信息,建立一个临时TCP端口tcpport0,当服务器端客户验证通过后向刚才的客户机tcpport0发送欢迎信息,这样客户机就得到了服务器的ip地址(还可同时进行一些必要的初始化),到此客户端便开始正式通过tcp与服务器交换游戏信息。

 

pyOpengl安装方法:http://pyopengl.sourceforge.net/documentation/installation.html

 

游戏代码:

 

#-*- coding:utf-8 -*-

import sys,struct
import random,math,thread,time,socket,string,re
try:
  from OpenGL.GLUT import *
  from OpenGL.GL import *
  from OpenGL.GLU import *
except:
  print '''
ERROR: PyOpenGL not installed properly.  
        '''
  sys.exit()
x_coord=10
y_coord=10
isAserver=False     #True为服务端,False为客户端

#数值统统为4个字节,最高位为符号位,1600万足矣,低字节为低位
def westicenumtostring(num):
    tempnum=abs(num)
    tempnum=int(tempnum)
    s=''
    i=0
    while tempnum>0:
       s=s+chr(tempnum%256)
       tempnum=int(tempnum/256) 
       i+=1
    while len(s)<3:#补齐3位
       s=s+chr(0)
    if num<0:
        s=s+chr(1)
    else:#0和正数最高位为0
        s=s+chr(0)
    return s

def westicestringtonum(s):
    if len(s)!=4:
        print 'len(s) must be 4'
        return None
    n0=ord(s[0])
    n1=ord(s[1])
    n2=ord(s[2])
    n3=ord(s[3])
    num=n0+256*n1+256*256*n2
    if n3==1:#负数
        num=(-num)
    if n3!=0 and n3!=1:
        return "wrong string when convert ro number"
    return num

def drawenemy(x,y): #画敌人形象
    glLoadIdentity()
    glTranslate(x,y,0.0)
    glScale(0.2,0.2,0.2)
    glBegin(GL_POLYGON)  
    glVertex3f(1.0,1.0,0)
    glVertex3f(1.0,-1.0,0)
    glColor3f(1.0,0.0,0.0)
    glVertex3f(-1.0,-1.0,0)
    glVertex3f(-1.0,1.0,0)
    glEnd()

def drawbullet(x,y):#画子弹形象
   glLoadIdentity()
   glTranslate(x,y,0.0)
   glScale(0.1,0.1,0.1)
   glBegin(GL_POLYGON)  
   glColor3f(0.0,1.0,0.0)          
   glVertex3f(0.5,1.0,0)
   glVertex3f(0.5,-1.0,0)
   glVertex3f(-0.5,-1.0,0)
   glVertex3f(-0.5,1.0,0)
   glEnd()

def drawpalne(x,y,rquad):#画玩家飞机形象
    glLoadIdentity()
    glTranslate(x,y,0.0)
    glRotatef(rquad,0.0,1.0,0.0)        # Rotate 
    glScale(0.5,0.5,0.5)
    glBegin(GL_POLYGON)                   # Start drawing 
    glColor3f(1.0,0.0,0.0)
    glVertex3f(1.0,0.0,0.0)       
    glColor3f(0.0,1.0,0.0)
    glVertex3f(-1.0,0.0,0.0)         
    glColor3f(0.0,0.0,1.0)
    glVertex3f(0.0,3.0,0.0)         
    glEnd()                             # We are done with the polygon
    
    glBegin(GL_POLYGON)  
    glVertex3f(0.0,0.0,0)
    glVertex3f(0.0,3.0,0)
    glColor3f(1.0,0.0,0.0)
    glVertex3f(0.0,0.5,0.5)
    glEnd()
    
class Enemy(): #定义敌人
    def __init__(self):
        self.reset()
    def update(self):
        if self.live:
           self.y-=0.01
           self.draw()
           if self.y<0:
               self.live=False 
    def setxy(self,x,y):
        self.x=x
        self.y=y
    def draw(self):
        drawenemy(self.x,self.y)
    def reset(self):
        self.x=x_coord*random.random()
        self.y=y_coord
        self.live=True #活着状态
        
class Bullet():#定义子弹
    def __init__(self,x,y):
        self.reset(x,y)
        self.live=False
    def update(self):
        if self.live:
           self.y+=0.05
           self.draw()
           if self.y>y_coord:
               self.live=False           
    def draw(self):
        drawbullet(self.x,self.y)
    def reset(self,x,y):
        self.x=x
        self.y=y
        self.live=True

class Plane():
    def __init__(self,x,y):
        self.x=x
        self.y=y
        self.rquad=0
    def update(self):
        self.draw()
        if self.rquad<0:
           self.rquad+=1.0
        if self.rquad>0:
           self.rquad-=1.0
    def draw(self):
        drawpalne(self.x,self.y,self.rquad)
    def left(self):
        self.x-=0.1
        if self.rquad>-40:#限制
           self.rquad-=3
    def right(self):
        self.x+=0.1
        if self.rquad<40:
           self.rquad+=3
           
westiceplane=None
myenemylist=None
bulletlist=None
otherplane=None
otherenemylist=None
otherbulletlist=None
frameobj=None
#网络用帧
class netframe():
    def __init__(self,player,mybulletlist,enemylist):
        self.player=player
        self.mybulletlist=mybulletlist
        self.enemylist=enemylist
    def senddata(self,conn):
        global isconnected
        senddata=''
        senddata+=westicenumtostring(self.player.x*1000)
        senddata+=westicenumtostring(self.player.y*1000) 
        senddata+=westicenumtostring(self.player.rquad*1000)#自己的位置
        senddata+=westicenumtostring(len(self.mybulletlist))#子弹个数
        for bullet in self.mybulletlist:
            senddata+=westicenumtostring(bullet.x*1000)
            senddata+=westicenumtostring(bullet.y*1000)#子弹位置
        senddata+=westicenumtostring(len(self.enemylist))#敌人个数
        for enemy in self.enemylist:
            senddata+=westicenumtostring(enemy.x*1000)
            senddata+=westicenumtostring(enemy.y*1000)#敌人位置
        if isconnected:
           try:     
              conn.send(senddata)
           except socket.error:
              isconnected=False
        #conn.sendall(senddata)
    #接收数据
    def recvdata(self,conn):
        global otherplane
        global otherenemylist
        global otherbulletlist
        global isconnected
        if isconnected:
            try:
                 recvdata=conn.recv(4)
                 otherplanex=westicestringtonum(recvdata)/1000.0
                 recvdata=conn.recv(4)
                 otherplaney=westicestringtonum(recvdata)/1000.0
                 otherplane=Plane(otherplanex,otherplaney)
                 recvdata=conn.recv(4)
                 otherplane.rquad=westicestringtonum(recvdata)/1000.0
                 recvdata=conn.recv(4)#接收子弹数据
                 bulletnum=westicestringtonum(recvdata)
                 otherbulletlist=[]
                 for i in range(bulletnum):
                     recvdata=conn.recv(4)
                     bulletx=westicestringtonum(recvdata)/1000.0
                     recvdata=conn.recv(4)
                     bullety=westicestringtonum(recvdata)/1000.0
                     bullet=Bullet(bulletx,bullety)
                     bullet.live=True
                     otherbulletlist.append(bullet)
                     
                 recvdata=conn.recv(4)
                 enemynum=westicestringtonum(recvdata)
                 otherenemylist=[]
                 for i in range(enemynum):
                     recvdata=conn.recv(4)
                     enemyx=westicestringtonum(recvdata)/1000.0
                     recvdata=conn.recv(4)
                     enemyy=westicestringtonum(recvdata)/1000.0
                     enemy=Enemy()    
                     enemy.setxy(enemyx,enemyy)
                     otherenemylist.append(enemy)
            except socket.error:
                isconnected=False
    
#游戏服务器
port=8088  #主数据通道
conn=None#socket连接对象,可能是服务器或客户端
serverUDPport=9090  
clientTCPport=9091  
isconnected=False
class GameServer():
    def __init__(self):
        print '初始化游戏服务器'
        global conn
        global otherplane
        global otherenemylist
        global otherbulletlist
        global frameobj
        global isconnected
        global serverUDPport
        global clientTCPport
        
        #服务器接收客户UDP报文,如果验证通过向客户发送TCP回应
        UDPsock0=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)    
        UDPsock0.bind(("",serverUDPport))
        #还需验证客户端,用udp
        recvstring,clientaddr=UDPsock0.recvfrom(1024)
        if recvstring=="west":
            print "服务器收到验证from ",clientaddr[0]
        UDPsock0.close()
        
        #服务器发送tcp回应
        tempconn=socket.socket(socket.AF_INET,socket.SOCK_STREAM)  
        tempconn.connect((clientaddr[0],clientTCPport))
        tempconn.send("sure")
        tempconn.close()
        
        #创建TCP服务器
        TCPsock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        TCPsock.bind(('',port))
        TCPsock.listen(5)        
        conn,clientaddr=TCPsock.accept()
        print "来自客户端:",clientaddr
        isconnected=True    
        while isconnected: #服务端循环,先发后收
            frameobj.senddata(conn)
            time.sleep(0.02)
            frameobj.recvdata(conn)
        conn.close()
            
#游戏客户端
class GameClient():
    def __init__(self):
        print '初始化游戏客户端'
        global conn
        global frameobj
        global isconnected
        global serverUDPport
        global clientTCPport
        self.lanserverip=None
        #搜索服务器,向局域网内发送udp数据
        ip=socket.gethostbyname(socket.gethostname())
        match_str="\d+\.\d+\.\d+\."
        ipheader=re.match(match_str,ip)
        ipheader=ipheader.group()
        UDPsock0=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)    
        for i in range(1,256):
            self.lanserverip=ipheader+str(i)
            UDPsock0.sendto("west",(self.lanserverip,serverUDPport))
        UDPsock0.close()
        
        #客户端建立tcp服务器  接收服务器ip和其它信息,之后断开,连接服务器的tcp
        TCPsock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        TCPsock.bind(('',clientTCPport))
        TCPsock.listen(5)    
        tempconn,serveraddr=TCPsock.accept()
        hellomsg=tempconn.recv(4)
        tempconn.close()
        self.lanserverip=serveraddr[0]
        print "服务器为:",self.lanserverip
        print "服务器信息:",hellomsg
        
        #连接tcp服务器发送数据
        conn=socket.socket(socket.AF_INET,socket.SOCK_STREAM)  
        conn.connect((self.lanserverip,port))
        isconnected=True
        while isconnected:#客户端循环,先收后发
            frameobj.recvdata(conn)
            frameobj.senddata(conn)
            time.sleep(0.02)
        conn.close()

def threadfunc():#线程函数
    global isAserver
    if isAserver:
        gameserver=GameServer()
    else:
        gameclient=GameClient()

def GameInit():
    global x_coord
    global y_coord
    global westiceplane
    global myenemylist
    global mybulletlist
    global frameobj
    westiceplane=Plane(x_coord/2,1)#加入飞机
    myenemylist=[]
    for i in range(4):  #加入敌人
        westiceenemy=Enemy()
        myenemylist.append(westiceenemy)
    mybulletlist=[]
    for i in range(20):
        mywesticebullet=Bullet(westiceplane.x,westiceplane.y)
        mybulletlist.append(mywesticebullet)
    #新建一个线程处理网络
    thread.start_new_thread(threadfunc,())
    frameobj=netframe(westiceplane,mybulletlist,myenemylist)
    
def init():
    glClearColor(0.5,0.5,0.5,0.0)
    glClearDepth(1.0)                    # Enables Clearing Of The Depth Buffer
    glDepthFunc(GL_LESS)                # The Type Of Depth Test To Do
    glEnable(GL_DEPTH_TEST)                # Enables Depth Testing
    glShadeModel(GL_SMOOTH)                # Enables Smooth Color Shading

def calcdistance(object0,object1):
    return math.sqrt(pow(object0.x-object1.x,2)+pow(object0.y-object1.y,2))

count=0
def display():
    #print 'display'
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
    westiceplane.update()
    for enemy in myenemylist:
        enemy.update()
        if not enemy.live:
            enemy.reset() 
        for bullet in mybulletlist:
            if calcdistance(bullet,enemy)<=0.2:#是否相遇
                bullet.live=False
                enemy.live=False
    #自动发射子弹
    global count
    if count>=15:
        count=0
        for bullet in mybulletlist:
            if not bullet.live:
                bullet.reset(westiceplane.x,westiceplane.y+1)#激活一颗
                break
    
    for bullet in mybulletlist:
        bullet.update()    
    count+=1
    #显示网络来的数据
    global otherplane
    global otherenemylist
    global otherbulletlist
    if isconnected and otherplane:
       otherplane.draw()
    if isconnected and otherbulletlist and otherenemylist and otherplane:
        otherplane.draw()
        for myenemy in myenemylist:
            for otherbullet in otherbulletlist:
                if calcdistance(otherbullet,myenemy)<=0.2:#是否相遇
                   otherbullet.live=False    
                   myenemy.live=False   
        for otherenemy in otherenemylist:
            if otherenemy.live:
               otherenemy.draw()
        for otherbullet in otherbulletlist:
            if otherbullet.live:
               otherbullet.draw()
    
    glutSwapBuffers()
    #glFlush()

def reshape(w,h):
   print 'reshape'
   glViewport(0,0,w,h)
   glMatrixMode(GL_PROJECTION)
   glLoadIdentity()
   print 'w:',w,' h:',h
   if w!=0 and h!=0:
       global x_coord
       global y_coord
       if(w<=h):
          gluOrtho2D(0.0,x_coord,0.0,x_coord*h/w)
       else:
          gluOrtho2D(0.0,x_coord*w/h,0.0,x_coord)
   glMatrixMode(GL_MODELVIEW)

def keyboard(key,x,y):
#   if key==chr(27):
#      sys.exit(0)
    if key=='a'or key=='A':
       westiceplane.left()
    if key=='d'or key=='D':
       westiceplane.right()


GameInit()
glutInit(sys.argv)
glutInitDisplayMode(GLUT_RGBA|GLUT_DOUBLE|GLUT_DEPTH) #使用双缓冲和深度测试
glutInitWindowSize(600,500) 
glutInitWindowPosition(100,100)
if isAserver:
    glutCreateWindow('Game Server')
else:
    glutCreateWindow('Game Client')
init()
glutReshapeFunc(reshape)
glutKeyboardFunc(keyboard)
glutDisplayFunc(display)
glutIdleFunc(display)

glutMainLoop()
 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics