微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

pygame中的多人游戏程序在添加一些代码后运行非常慢

如何解决pygame中的多人游戏程序在添加一些代码后运行非常慢

我有一个pygame / socket程序,它基本上是一个迷宫游戏,您必须在其中“标记”其他玩家,但是当我添加发送代码时,该程序的运行速度大大降低,该程序处于全屏状态,因此我添加了另一个套接字发送行以确保搜索器相同,然后停止快速运行。听到我的代码

我的第一个目标:

import pygame
import time
import random
import socket
import struct
pygame.init()
s = socket.socket()
port = 12345
s.bind(('',port))
s.listen(5)
c,addr = s.accept()
print ("Socket Up and running with a connection from",addr)
screen = pygame.display.set_mode((0,0),pygame.FULLSCREEN)
blocks = [(50,50,200,25),(200,25,250),(300,(50,200),(250,100,250,(350,225,(75,325,(25,150,(100,75,100),375,175,425,75),500,450,150),(150,550,300,175),600,(0,400,650,(125,625,750,10000,28),(1350,16,10000),(225,125),(175,700,(275,125,50),(500,300),(400,225),(425,(450,275),500),(550,475),575,(325,(375,(475,(525,(600,525,(650,(850,475,675,(700,725,(900,(950,(800,275,(750,(875,350,(575,(925,(1025,(1050,(1125,375),(1075,(1175,(1150,(1300,(1250,(1000,(1100,(1200,25)
          ]
walls = [pygame.Rect(r) for r in blocks]
svel = 5
Hvel = 5
red = pygame.Rect(225,25)
blue = pygame.Rect(1100,25)
SeekChoice = random.randint(1,2)
if SeekChoice == 1:
    seeker = "red"
    Snum = 2
if SeekChoice == 2:
    seeker = "blue"
    Snum = 1
Num = 0
c.send(struct.pack("2i",SeekChoice,Num))
Redscore = 0
Bluescore = 0
RTime = 0
BTime = 0
running = True
font = pygame.font.Font(None,60)
RedWin = font.render("RED IS WINNER!",(255,0))
BlueWin = font.render("BLUE IS WINNER!",255))
while running:
    buf = c.recv(8,socket.MSG_WAITALL)
    struct.unpack("2i",buf)
    x,y = struct.unpack("2i",buf)
    red = pygame.Rect(x,y,25)
    
    if Redscore >= 20:
        screen.fill([255,255,255])
        screen.blit(RedWin,[500,250])
        pygame.display.flip()
        time.sleep(10)
        pygame.quit()
        running = False
    if Bluescore >= 20:
        screen.fill([255,255])
        screen.blit(BlueWin,250])
        pygame.display.flip()
        time.sleep(10)
        pygame.quit()
        running = False
    RTime += 1
    BTime += 1
    x1 = red.x
    y1 = red.y
    x2 = blue.x
    y2 = blue.y
    
    if seeker == "red":
        RTime = 0
    elif RTime >= 2000:
        RTime = 0
        Redscore += 1
    if seeker == "blue":
        BTime = 0
    elif BTime >= 2000:
        BTime = 0
        Bluescore += 1
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_ESCAPE:
                pygame.quit()
                running = False
    keys = pygame.key.get_pressed()
    if keys[pygame.K_LEFT]:
        blue.x = max(blue.x - svel,0)
        for wall in walls: 
            if blue.colliderect(wall):
                blue.left = max(blue.left,wall.right)
    if keys[pygame.K_RIGHT]:
        blue.x = min(blue.x + svel,1341)
        for wall in walls: 
            if blue.colliderect(wall):
                blue.right = min(blue.right,wall.left)
    if keys[pygame.K_UP]:
        blue.y = max(blue.y - svel,0)
        for wall in walls: 
            if blue.colliderect(wall):
                blue.top = max(blue.top,wall.bottom)
    if keys[pygame.K_DOWN]:
        blue.y = min(blue.y + svel,743)
        for wall in walls: 
            if blue.colliderect(wall):
                blue.bottom = min(blue.bottom,wall.top)
    screen.fill([0,0])
    #pygame.draw.circle(screen,[255,255],(x1+12,y1+12),125)
    pygame.draw.circle(screen,(x2+12,y2+12),125)
    for wall in walls: 
        pygame.draw.rect(screen,[0,0],wall)
    #pygame.draw.rect(screen,red)
    pygame.draw.rect(screen,blue)
    if seeker == "red":
        pygame.draw.rect(screen,(red.x+7,red.y+7,10,10))
    if seeker == "blue":
        pygame.draw.rect(screen,(blue.x+7,blue.y+7,10))
    pygame.display.flip()
    if red.colliderect(blue):
        if seeker == "red":
            print ("red/blue")
            seeker = "blue"
            Snum = 1
            BTime = 0
            Redscore += 2
            Bluescore -= 3
            red = pygame.Rect(225,25)
            blue = pygame.Rect(1166,334,25)
        elif seeker == "blue":
            print ("blue/red")
            Snum = 2
            RTime = 0
            seeker = "red"
            Bluescore += 2
            Redscore -= 3
            red = pygame.Rect(225,25)
    seeker2 = seeker.encode()
    c.send(seeker2)
    c.send(struct.pack("2i",blue.x,blue.y))

我的第二个目的:

import pygame
import time
import random
import socket
import struct
pygame.init()

s = socket.socket()
s.connect(('192.168.43.188',12345))

screen = pygame.display.set_mode((0,25)
          ]

walls = [pygame.Rect(r) for r in blocks]
svel = 5
Hvel = 5
red = pygame.Rect(225,25)
buf = s.recv(8,socket.MSG_WAITALL)
SeekChoice,Num = struct.unpack("2i",buf)
if SeekChoice == 1:
    seeker = "red"
    Snum = 1
if SeekChoice == 2:
    seeker = "blue"
    Snum = 2
Redscore = 0
Bluescore = 0
RTime = 0
BTime = 0
running = True
font = pygame.font.Font(None,255))

screen.fill([255,255])
pygame.draw.circle(screen,(red.x+12,red.y+12),75)
pygame.draw.circle(screen,(blue.x+12,blue.y+12),75)
for wall in walls: 
    pygame.draw.rect(screen,wall)
pygame.draw.rect(screen,red)
pygame.draw.rect(screen,blue)
if seeker == "red":
    pygame.draw.rect(screen,10))
if seeker == "blue":
    pygame.draw.rect(screen,10))
pygame.display.flip()
while running:
    if Snum == 1:
        seeker = "red"
    if Snum == 2:
        seeker = "blue"
    s.send(struct.pack("2i",red.x,red.y))
    if Redscore >= 20:
        screen.fill([255,250])
        pygame.display.flip()
        time.sleep(10)
        pygame.quit()
        running = False
    RTime += 1
    BTime += 1

    if seeker == "red":
        RTime = 0
    elif RTime >= 2000:
        RTime = 0
        Redscore += 1
    if seeker == "blue":
        BTime = 0
    elif BTime >= 2000:
        BTime = 0
        Bluescore += 1
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_ESCAPE:
                pygame.quit()
                running = False
    keys = pygame.key.get_pressed()
    if keys[pygame.K_LEFT]:
        red.x = max(red.x - svel,0)
        for wall in walls: 
            if red.colliderect(wall):
                red.left = max(red.left,wall.right)
    if keys[pygame.K_RIGHT]:
        red.x = min(red.x + svel,1341)
        for wall in walls: 
            if red.colliderect(wall):
                red.right = min(red.right,wall.left)
    if keys[pygame.K_UP]:
        
        red.y = max(red.y - svel,0)
        for wall in walls: 
            if red.colliderect(wall):
                red.top = max(red.top,wall.bottom)
    if keys[pygame.K_DOWN]:
        red.y = min(red.y + svel,743)
        for wall in walls: 
            if red.colliderect(wall):
                red.bottom = min(red.bottom,0])
    pygame.draw.circle(screen,125)
    #pygame.draw.circle(screen,wall)
    pygame.draw.rect(screen,red)
    #pygame.draw.rect(screen,10))
    pygame.display.flip()
    seeker = s.recv(1024)
    seeker = seeker.decode()
    buf = s.recv(8,socket.MSG_WAITALL)
    x,buf)
    blue = pygame.Rect(x,25)
    red = pygame.Rect(red.x,red.y,25)

感谢您的帮助。

解决方法

您的套接字处理代码(可能)是导致速度下降的原因。 socket.recv()正在等待数据到达。当然,您可以将套接字设置为非阻塞读取,但是仍然存在是否已到达完整数据包的问题。

我会将您的代码转换为使用固定的数据包大小。与其发送可变长度的颜色名称,不如发送一个颜色元组。发送[250 250 210],然后发送“浅金黄色”要快得多;)我想您可以只使用[red ][blue]

一旦您确定了数据包的大小,则可以准确地知道何时有新数据包到达,而不是说半个数据包。当然,对于本地网络上没有微小碎片的小数据包来说,这并不是真正的问题。但是数据量增加,网络拓扑发生变化,出现碎片。

我喜欢使用select模块(紧密模拟同名的C系统函数)来知道是否有任何数据到达套接字,如果没有任何内容,则只需返回主程序循环。但是,如果有可用数据,则将任何内容读入缓冲区。这为您提供了一个程序模型,您可以在其中读取任何已到达的内容(大多数情况下什么都不读取),并且如果“到达的”缓冲区中有足够的字节,则取出足够的字节以形成完整的数据包。它简单,强大且运行良好。

给定功能:

import socket
import select

def socketReadIfAvailable( read_socket:socket.socket,packet_buffer:bytearray,amount:int=1,timeout:float=0 ):
    """ Given an open socket,and a packet-buffer,return a packet if one is available. 
        Returns a tuple of: 
            None        - if no packet is available
            data-packet - (of <amount> bytes) if data is available (of <amount> bytes length)
            -1          - if the socket has closed  
            AND the current packet-buffer """
    result = None
    # If we already have enough data buffered,don't re-fetch
    if ( len( packet_buffer ) >= amount ):
        bytes_read  = packet_buffer[0:amount]
        packet_buffer = packet_buffer[amount:]
        result = bytes_read
    else:
        # There's not enough data,so try to read some,but only for <timeout> seconds
        # if timeout is zero,just read whatever is sitting in the socket buffer already
        read_events_on   = [ read_socket ]
        (read_list,write_list,except_list) = select.select( read_events_on,[],timeout )

        if ( len( read_list ) > 0 ):
            # New data arrived,read it
            incoming = read_socket.recv( 8192 )
            if ( len( incoming ) == 0 ):
                # No data arrived,meaning the socket has closed
                result = -1
            else:
                #print("%d bytes Rx'd" % ( len( incoming ) ) )
                #print("%d bytes buffered" % ( len( packet_buffer ) ) )
                packet_buffer += incoming  # new bytes arrived,is there enough data now?
                if ( len( packet_buffer ) >= amount ):
                    bytes_read  = packet_buffer[0:amount]
                    packet_buffer = packet_buffer[amount:]
                    result = bytes_read
    return result,packet_buffer

一个11字节的固定大小的数据包(RGB颜色为3字节,每个坐标为4字节),您的读取代码可能类似于:

packet_buffer = bytearray()

...

pygame.display.flip()
packet,packet_buffer = socketReadIfAvailable( s,packet_buffer,11 )
if ( packet != None ):
    if ( packet == -1 ):
        # TODO: handle Socket has closed
        packet_buffer = bytearray()  # clear buffer            
    else:
        colour = struct.unpack( "!BBB",packet[0:3] )   # use network byte-order
        x,y    = struct.unpack( "!2i",packet[3:11] )

当然,如果您不想使用固定大小,则可以仅查看是否有任何数据到达,然后try:对其进行解包(或整理),并希望获得最好的结果。不过,这效率相对较低。

编辑

通常我会尽量不成为免费的代码编写服务,但是我对有趣的外观原型游戏深信不疑;)

好吧,我不确定您的游戏应该如何运作。我发现整个redblue变量集令人困惑。我认为您应该对代码进行重新设计,以使其具有不带GUI的单个服务器,而只是在客户端之间分流更新的位置。然后,您可以拥有一个游戏代码,其中两个玩家(或 N 玩家)都连接到该服务器。这也会从您的代码中删除redblue VS x,留下“本地”和“其他”(或您想称呼它们的任何东西)。

我修改了您的代码,使其仅在yimport pygame import time import random import socket import struct import select network_buffer = bytearray() def socketReadIfAvailable( read_socket:socket.socket,return a packet if one is available. Returns a tuple of: None - if no packet is available data-packet - (of <amount> bytes) if data is available (of <amount> bytes length) -1 - if the socket has closed AND the current packet-buffer """ result = None # If we already have enough data buffered,packet_buffer pygame.init() s = socket.socket() port = 12345 s.bind(('',port)) s.listen(5) c,addr = s.accept() print ("Socket Up and running with a connection from",addr) screen = pygame.display.set_mode( ( 1200,960 ) ) pygame.display.set_caption("Maze Server") blocks = [(50,50,200,25),(200,25,250),(300,(50,200),(250,100,250,(350,225,(75,325,(25,150,(100,75,100),375,175,425,75),500,450,150),(150,550,300,175),600,(0,400,650,(125,625,750,10000,28),(1350,16,10000),(225,125),(175,700,(275,125,50),(500,300),(400,225),(425,(450,275),500),(550,475),575,(325,(375,(475,(525,(600,525,(650,(850,475,675,(700,725,(900,(950,(800,275,(750,(875,350,(575,(925,(1025,(1050,(1125,375),(1075,(1175,(1150,(1300,(1250,(1000,(1100,(1200,25) ] x,y = 0,0 sent_x,sent_y = -1,-1 walls = [pygame.Rect(r) for r in blocks] Svel = 5 Hvel = 5 red = pygame.Rect(225,25) blue = pygame.Rect(1100,25) SeekChoice = random.randint(1,2) if SeekChoice == 1: seeker = "red" Snum = 2 if SeekChoice == 2: seeker = "blue" Snum = 1 Num = 0 c.send(struct.pack("2i",SeekChoice,Num)) RedScore = 0 BlueScore = 0 RTime = 0 BTime = 0 running = True font = pygame.font.Font(None,60) RedWin = font.render("RED IS WINNER!",(255,0)) BlueWin = font.render("BLUE IS WINNER!",255)) while running: ### Receive a fixed-size packet from the client (if any) ### Client only sends us an x,y #buf = c.recv(8,socket.MSG_WAITALL) buf,network_buffer = socketReadIfAvailable( s,network_buffer,8 ) # 8 bytes in a full packet if ( buf != None ): # *IF* a packet was received if ( buf == -1 ): print( "Client disconnected" ) network_buffer = bytearray() # TODO else: # A packet was received,unpack it x,y = struct.unpack( "2i",buf ) # unpack the co-ordinates print( "Rx: (%d,%d)" % ( x,y ) ) red = pygame.Rect(x,y,25) if RedScore >= 20: screen.fill([255,255,255]) screen.blit(RedWin,[500,250]) pygame.display.flip() time.sleep(10) pygame.quit() running = False if BlueScore >= 20: screen.fill([255,255]) screen.blit(BlueWin,250]) pygame.display.flip() time.sleep(10) pygame.quit() running = False RTime += 1 BTime += 1 x1 = red.x y1 = red.y x2 = blue.x y2 = blue.y if seeker == "red": RTime = 0 elif RTime >= 2000: RTime = 0 RedScore += 1 if seeker == "blue": BTime = 0 elif BTime >= 2000: BTime = 0 BlueScore += 1 for event in pygame.event.get(): if event.type == pygame.QUIT: running = False if event.type == pygame.KEYDOWN: if event.key == pygame.K_ESCAPE: pygame.quit() running = False keys = pygame.key.get_pressed() if keys[pygame.K_LEFT]: blue.x = max(blue.x - Svel,0) for wall in walls: if blue.colliderect(wall): blue.left = max(blue.left,wall.right) if keys[pygame.K_RIGHT]: blue.x = min(blue.x + Svel,1341) for wall in walls: if blue.colliderect(wall): blue.right = min(blue.right,wall.left) if keys[pygame.K_UP]: blue.y = max(blue.y - Svel,0) for wall in walls: if blue.colliderect(wall): blue.top = max(blue.top,wall.bottom) if keys[pygame.K_DOWN]: blue.y = min(blue.y + Svel,743) for wall in walls: if blue.colliderect(wall): blue.bottom = min(blue.bottom,wall.top) screen.fill([0,0]) #pygame.draw.circle(screen,[255,255],(x1+12,y1+12),125) pygame.draw.circle(screen,(x2+12,y2+12),125) for wall in walls: pygame.draw.rect(screen,[0,0],wall) #pygame.draw.rect(screen,red) pygame.draw.rect(screen,blue) # if seeker == "red": # pygame.draw.rect(screen,(red.x+7,red.y+7,10,10)) # if seeker == "blue": # pygame.draw.rect(screen,(blue.x+7,blue.y+7,10)) pygame.draw.rect(screen,10)) pygame.display.flip() if red.colliderect(blue): if seeker == "red": print ("red/blue") seeker = "blue" Snum = 1 BTime = 0 RedScore += 2 BlueScore -= 3 red = pygame.Rect(225,25) blue = pygame.Rect(1166,334,25) elif seeker == "blue": print ("blue/red") Snum = 2 RTime = 0 seeker = "red" BlueScore += 2 RedScore -= 3 red = pygame.Rect(225,25) ### Pack and send the fixed-size data to the client ### But only if the position x,y has changed if ( sent_x != blue.x or sent_y != blue.y ): seeker_name = "%-12s" % ( seeker ) # pad to 12 spaces,this never changes,move outside loop seeker_name = seeker_name.encode() # this too c.send( bytes( seeker_name ) ) # send the colour-name padded to 12 letters c.send( struct.pack("2i",blue.x,blue.y ) ) # send the x,y sent_x = blue.x sent_y = blue.y print( "Tx: [%s],(%d,%d)" % ( seeker,sent_x,sent_y ) ) 坐标更改时才发送位置。无需使用相同的不变坐标将网络垃圾邮件每秒60次。

还可以考虑添加注释,有时还可以在代码中添加空白行,这使跟踪变得更加容易。我将此添加到其中一个文件中。

服务器:

import pygame
import time
import random
import socket
import struct
pygame.init()


import select
network_buffer = bytearray()

def socketReadIfAvailable( read_socket:socket.socket,packet_buffer




s = socket.socket()
s.connect(('127.0.0.1',12345))

screen = pygame.display.set_mode( ( 1200,960 ) )
pygame.display.set_caption("Maze Client")

blocks = [(50,25)
          ]

sent_x,25)
buf = s.recv(8,socket.MSG_WAITALL)
SeekChoice,Num = struct.unpack("2i",buf)
if SeekChoice == 1:
    seeker = "red"
    Snum = 1
if SeekChoice == 2:
    seeker = "blue"
    Snum = 2
RedScore = 0
BlueScore = 0
RTime = 0
BTime = 0
running = True
font = pygame.font.Font(None,255))

#screen.fill([255,255])
#pygame.draw.circle(screen,(red.x+12,red.y+12),75)
#pygame.draw.circle(screen,(blue.x+12,blue.y+12),75)
#for wall in walls: 
#    pygame.draw.rect(screen,wall)
#pygame.draw.rect(screen,red)
#pygame.draw.rect(screen,blue)
#if seeker == "red":
#    pygame.draw.rect(screen,10))
#if seeker == "blue":
#    pygame.draw.rect(screen,10))
#pygame.display.flip()

while running:
#    if Snum == 1:
#        seeker = "red"
#    if Snum == 2:
#        seeker = "blue"

    ### Send the x,y position only if it's changed
    if ( sent_x != red.x or sent_y != red.y ):
        s.send(struct.pack("2i",red.x,red.y))
        sent_x = red.x
        sent_y = red.y
        print( "Tx: (%d,%d)" % ( sent_x,sent_y ) )

    if RedScore >= 20:
        screen.fill([255,250])
        pygame.display.flip()
        time.sleep(10)
        pygame.quit()
        running = False
    RTime += 1
    BTime += 1

    if seeker == "red":
        RTime = 0
    elif RTime >= 2000:
        RTime = 0
        RedScore += 1
    if seeker == "blue":
        BTime = 0
    elif BTime >= 2000:
        BTime = 0
        BlueScore += 1

    # Handle Events
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_ESCAPE:
                pygame.quit()
                running = False

    # Handle movement keys
    keys = pygame.key.get_pressed()
    if keys[pygame.K_LEFT]:
        red.x = max(red.x - Svel,0)
        for wall in walls: 
            if red.colliderect(wall):
                red.left = max(red.left,wall.right)
    if keys[pygame.K_RIGHT]:
        red.x = min(red.x + Svel,1341)
        for wall in walls: 
            if red.colliderect(wall):
                red.right = min(red.right,wall.left)
    if keys[pygame.K_UP]:
        red.y = max(red.y - Svel,0)
        for wall in walls: 
            if red.colliderect(wall):
                red.top = max(red.top,wall.bottom)
    if keys[pygame.K_DOWN]:
        red.y = min(red.y + Svel,743)
        for wall in walls: 
            if red.colliderect(wall):
                red.bottom = min(red.bottom,wall.top)

    # Paint the screen
    screen.fill([0,0])
    pygame.draw.circle(screen,125)
    #pygame.draw.circle(screen,wall)
    pygame.draw.rect(screen,red)
    #pygame.draw.rect(screen,10))
    pygame.display.flip()

    #seeker = s.recv(1024)
    #seeker = seeker.decode()
    #buf = s.recv(8,socket.MSG_WAITALL)
    #x,y = struct.unpack("2i",buf)

    ### Receive a fixed-size packet from the server (if any)
    ### Server sends a 12-letter name,and x,y
    buf,20 ) # 20 bytes in a full packet
    if ( buf != None ):   # *IF* a packet was received
        if ( buf == -1 ):
            print( "Client disconnected" )
            network_buffer = bytearray()
            # TODO
        else:
            # A packet was received,unpack it
            seeker = buf[0:12].decode( "utf-8" ) # unpack the 12-letter (padded) name
            seeker = seeker.strip()
            x,buf[12:20] )  # unpack the co-ordinates
            blue = pygame.Rect(x,25)
            print( "Rx: [%s] at (%d,x,y ) )


    red  = pygame.Rect(red.x,red.y,25)

客户:

socketReadIfAvailable()

很显然,您可以将blocks移到一个公共文件中,也许也移到class Manager: def __init__(self,cls): self.cls=cls # … class PreManager: def __get__(self,cls,obj): return Manager(cls) class Model: objects=PreManager() 中。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。