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

波涛汹涌的在线蟒蛇游戏

如何解决波涛汹涌的在线蟒蛇游戏

我使用 pygame 和套接字在 python 中编写了一个简单的 Slither.io 克隆,但我遇到了三个问题:

  1. 当我独自在笔记本电脑上玩游戏时,游戏断断续续。每十秒我的游戏就会卡住一段时间(一毫秒)然后继续。这不是什么大问题,但很烦人。
  2. 当我在本地网络中的两台计算机上玩游戏时,我看到另一个玩家(另一条蛇)也断断续续。
  3. 最奇怪的问题是,当我在主笔记本电脑上运行服务器,然后在我的第二台笔记本电脑上运行游戏时,游戏开始并在几秒钟后崩溃。客户端上的调试器说pickle 数据在从服务器接收数据时被截断。但是,当我在第二台笔记本电脑上运行服务器程序并在主笔记本电脑上运行游戏时,一切正常。为什么?

我试过了:
问题一、客户端修改FPS,服务器修改time.sleep
问题2.更改服务器上的time.sleep
问题3.改变recv()方法的输入值


服务器代码

import socket
import threading
import pickle
import random
import time
import math

ip = socket.gethostbyname(socket.gethostname())
port = 5555

address = (ip,port)
server = socket.socket(socket.AF_INET,socket.soCK_STREAM)

snakes = []
colors = [(80,0),(0,80,140),(80,80),80)]
food = []

for i in range(0,300,1):
    food.append([random.randint(10,3290),random.randint(10,2090),100),10,1])

number_of_players = 0

def start(server,address):
    server.bind(address)

def new_clients(server):
    global number_of_players
    server.listen()
    while True:
        conn,addr = server.accept()
        thread = threading.Thread(target=client,args=(conn,addr,number_of_players))
        thread.start()
        number_of_players += 1
        print(f"Active connections: {threading.activeCount() - 1}")

def client(conn,player):
    print(f"New connection: {addr}\n")
    global snakes
    global colors
    global food
    try:
        snakes.append([])
        name = pickle.loads(conn.recv(1024))
        while True:
            verity = True
            x = random.randint(100,3200)
            y = random.randint(100,2000)
            for i in range(0,len(snakes),1):
                if snakes[i] != []:
                    if math.sqrt((snakes[i][0][0] - x)**2 + (snakes[i][0][1] - y)**2) > snakes[i][0][4]*2:
                        for a in range(0,len(snakes[i][1])):
                            if math.sqrt((snakes[i][1][a][0] - x)**2 + (snakes[i][1][a][1] - y)**2) < snakes[i][0][4]*2:
                                verity = False
                            if verity == False:
                                break
                    else:
                        verity = False
                    if verity == False:
                        break
            if verity == True:
                break

        snakes[player] = [[x,y,random.choice(colors),4,30,name,3],[]]
        conn.send(pickle.dumps(snakes[player]))
    except (ConnectionAbortedError,ConnectionResetError,EOFError):
        print("Connection lost")
        print(f"Active connections: {threading.activeCount() - 2}")
        conn.close()
        return
    while True:
        try:
            snakes_to_send = []
            for i in range(0,1):
                if (i != player) and (len(snakes[i]) != 0):
                    snakes_to_send.append(snakes[i])
            conn.send(pickle.dumps([snakes_to_send,food]))

            received = pickle.loads(conn.recv(8192*4))
            snakes[player] = received[0]

            eaten = received[1]
            for i in range(0,len(eaten),1):
                try:
                    food.remove(eaten[i])
                    if eaten[i][4] != 5:
                        food.append([random.randint(10,1])
                except ValueError:
                    continue

            dead_snake = received[2]
            if dead_snake != []:
                for i in range(0,len(dead_snake),1):
                    food.append(dead_snake[i])

            time.sleep(1/1000)
        except (ConnectionAbortedError,EOFError):
            snakes[player] = []
            print("Connection lost")
            print(f"Active connections: {threading.activeCount() - 2}")
            conn.close()
            break

print(f"Server is starting on IP: {ip} and PORT: {port}")
start(server,address)
new_clients(server)

游戏:

#import
import pygame
import sys
import math
import random
import pygame_textinput
from client import Client

#pygame initialize
pygame.init()
win = pygame.display.set_mode((1100,700))
pygame.display.set_caption("Slither.io")

#connect to the server
ip = "10.0.0.3"
port = 5555
c = Client(ip,port)
c.server()

#fonts
font = pygame.font.SysFont("comicsansms",20)
big_font = pygame.font.SysFont("comicsansms",35)

#time
clock = pygame.time.Clock()

class Snake:
    def __init__(self,x,color,length,diameter,score,step):
        self.x = x
        self.y = y
        self.color = color
        self.length = length
        self.diameter = diameter
        self.name = name
        self.score = score
        self.step = step
        self.pos_list = []

    #move the snake
    def move(self):
        self.mousex,self.mousey = pygame.mouse.get_pos()
        self.difx = self.mousex - 550
        self.dify = self.mousey - 350
        try:
            self.xstep = math.sqrt((self.steP**2)/((self.dify/self.difx)**2 + 1))
            self.ystep = (self.dify/self.difx)*self.xstep
        except ZeroDivisionError:
            self.xstep = 0
            self.ystep = self.step
        if math.sqrt(self.difx**2 + self.dify**2) > self.diameter/3:
            if(self.difx > 0):
                self.x += self.xstep
                self.y += self.ystep
            elif(self.difx < 0):
                self.x -= self.xstep
                self.y -= self.ystep
            else:
                if self.dify > 0:
                    self.y += self.ystep
                else:
                    self.y -= self.ystep
        self.pos_list.append([self.x,self.y])
        if len(self.pos_list) == 100:
            del self.pos_list[0]

    #check if the snake isn't out of the area or not crashed
    def collision(self):
        global run
        if (self.x + self.diameter/3 >= 3299) or (self.x - self.diameter/3 <= 1) or (self.y + self.diameter/3 >= 2099) or (self.y - self.diameter/3 <= 1):
            run = False
        for i in range(0,len(other_snakes),1):
            if math.sqrt((other_snakes[i].x - snake.x)**2 + (other_snakes[i].y - snake.y)**2) < other_snakes[i].diameter*(2/3):
                run = False
        for i in range(0,len(other_segments),1):
            if math.sqrt((other_segments[i].x - snake.x)**2 + (other_segments[i].y - snake.y)**2) < other_segments[i].diameter*(2/3):
                run = False

    #draw all snakes
    def draw(self):
        pygame.draw.rect(win,self.color,[self.x - snake.x + 550 - self.diameter/2,self.y - snake.y + 350 - self.diameter/2,self.diameter,self.diameter],border_radius=int(self.diameter/2))

class Segment(Snake):
    def __init__(self,step,circle):
        self.x = x
        self.y = y
        self.color = (80,80)
        self.diameter = diameter
        self.step = step
        self.circle = circle
        self.pos_list = []

    def move(self):
        self.diameter = snake.diameter
        try:
            self.x = self.circle.pos_list[-int(self.diameter/self.step)][0]
            self.y = self.circle.pos_list[-int(self.diameter/self.step)][1]
        except IndexError:
            self.x = self.circle.pos_list[0][0]
            self.y = self.circle.pos_list[0][1]
        self.pos_list.append([self.x,self.y])
        if len(self.pos_list) == 100:
            del self.pos_list[0]

class Food(Snake):
    def __init__(self,points):
        self.x = x
        self.y = y
        self.color = color
        self.diameter = diameter
        self.points = points

other_snakes = []
segments = []
other_segments = []
food = []
eaten = []
dead_snake = []

#get other snakes
def return_snakes():
    global other_snakes
    global other_segments
    global food
    other_snakes = []
    other_segments = []
    food = []
    message = c.receive_message()
    received = message[0]
    for i in range(0,len(received),1):
        s = Snake(received[i][0][0],received[i][0][1],received[i][0][2],received[i][0][3],received[i][0][4],received[i][0][5],received[i][0][6],snake.step)
        other_snakes.append(s)
        for a in range(0,len(received[i][1]),1):
            sg = Segment(received[i][1][a][0],received[i][1][a][1],s.diameter,snake.step,0)
            other_segments.append(sg)
    rec_fd = message[1]
    for i in range(0,len(rec_fd),1):
        f = Food(rec_fd[i][0],rec_fd[i][1],rec_fd[i][2],rec_fd[i][3],rec_fd[i][4])
        food.append(f)

#send the snake to server
def send_snake():
    global snake
    global segments
    global food
    to_send = [[[snake.x,snake.y,snake.color,snake.length,snake.diameter,snake.name,snake.score],[]]]
    for i in range(0,len(segments),1):
        to_send[0][1].append([segments[i].x,segments[i].y])
    to_send.append(eaten)
    to_send.append(dead_snake)
    c.send_message(to_send)

#function for ordering snakes by score
def order(o):
    return o["score"]

#draw screen
def screen():
    for i in range(0,3301,100):
        pygame.draw.line(win,(60,60,60),[i - snake.x + 550,0 - snake.y + 350],2100 - snake.y + 350],1)
    for i in range(0,2101,[0 - snake.x + 550,i - snake.y + 350],[3300 - snake.x + 550,1)

#enter your nickname
textinput = pygame_textinput.TextInput(font_family="comicsansms",max_string_length=12)
while True:
    win.fill((100,100,100))
    events = pygame.event.get()
    for event in events:
        keys = pygame.key.get_pressed()
        if (event.type == pygame.QUIT) or keys[pygame.K_ESCAPE] or keys[pygame.K_F4] and pygame.key.get_mods() & pygame.KMOD_ALT:
            sys.exit()
    if keys[pygame.K_RETURN]:
        break
    name = big_font.render("Nickname:",True,0))
    center = name.get_rect(center=(400,350))
    win.blit(name,center)
    textinput.update(events)
    win.blit(textinput.get_surface(),(500,325))
    pygame.display.update()

c.send_message(textinput.get_text())
received = c.receive_message()
snake = Snake(received[0][0],received[0][1],received[0][2],received[0][3],received[0][4],received[0][5],received[0][6],received[0][7])

segment = Segment(snake.x,snake)
segments.append(segment)
for i in range(0,snake.length - 2,1):
    segment = Segment(snake.x,segment)
    segments.append(segment)

while True:
    run = True
    while run:
        eaten = []
        #quit game
        for event in pygame.event.get():
            keys = pygame.key.get_pressed()
            if (event.type == pygame.QUIT) or keys[pygame.K_ESCAPE] or keys[pygame.K_F4] and pygame.key.get_mods() & pygame.KMOD_ALT:
                sys.exit()

        return_snakes()

        snakes = [{"name": snake.name,"score": snake.score}]
        for i in range(0,1):
            snakes.append({"name": other_snakes[i].name,"score": other_snakes[i].score})

        win.fill((100,100))
        screen()

        #draw all food
        for i in range(0,len(food),1):
            food[i].draw()

        #draw the other snakes and describe
        for i in range(0,1):
            other_segments[i].draw()

        for i in range(0,1):
            other_snakes[i].draw()

            name = font.render(other_snakes[i].name,0))
            center = name.get_rect(center=(other_snakes[i].x - snake.x + 550,other_snakes[i].y - snake.y + 350))
            win.blit(name,center)

        #move,draw snake and check if the snake is alive
        snake.move()
        for i in range(0,1):
            segments[i].move()
        for i in range(0,1):
            segments[i].draw()
        snake.draw()
        snake.collision()

        #check if the snake ate food and add score
        for i in range(len(food) - 1,-1,-1):
            if math.sqrt((food[i].x - snake.x)**2 + (food[i].y - snake.y)**2) < (snake.diameter/2 + food[i].diameter/2):
                snake.score += food[i].points
                snake.length = int(snake.score/10) + 4
                if snake.length - 1 > len(segments):
                    segment = Segment(snake.x,segment)
                    segments.append(segment)
                snake.diameter = int(snake.score/50)*0.5 + 30
                eaten.append([food[i].x,food[i].y,food[i].color,food[i].diameter,food[i].points])

        #describe the snake
        name = font.render(snake.name,0))
        center = name.get_rect(center=(550,350))
        win.blit(name,center)

        send_snake()

        #sort the snakes by score
        snakes.sort(key=order,reverse=True)

        #create the table
        for i in range(0,1):
            table = font.render(str(snakes[i]["name"]),0))
            win.blit(table,(900,50 + i*30))
        for i in range(0,1):
            table = font.render(str(snakes[i]["score"]),(1050,1):
            table = font.render(str(i + 1) + ".",(870,50 + i*30))

        pygame.display.update()
        clock.tick(140)

    return_snakes()

    #create food from head of dead snake
    ds = [snake.x,15,5]
    dead_snake.append(ds)

    #create food from segments of dead snake
    for i in range(0,1):
        ds = [segments[i].x,segments[i].y,5]
        dead_snake.append(ds)

    send_snake()
    dead_snake = []

    clock.tick(140)

    c.client.close()

    while True:
        #quit game
        for event in pygame.event.get():
            keys = pygame.key.get_pressed()
            if (event.type == pygame.QUIT) or keys[pygame.K_ESCAPE] or keys[pygame.K_F4] and pygame.key.get_mods() & pygame.KMOD_ALT:
                sys.exit()
        if keys[pygame.K_SPACE]:
            c = Client(ip,port)
            c.server()
            c.send_message(snake.name)
            received = c.receive_message()
            snake = Snake(received[0][0],received[0][7])
            segments = []
            segment = Segment(snake.x,snake)
            segments.append(segment)
            for i in range(0,1):
                segment = Segment(snake.x,segment)
                segments.append(segment)
            break

        win.fill((100,100))
        screen()

        #press space bar to continue
        cont = big_font.render("Press space bar to continue",0))
        center = cont.get_rect(center=(550,350))
        win.blit(cont,center)

        pygame.display.update()
        clock.tick(140)

客户端类文件

import socket
import pickle

ip = "10.0.0.3"
port = 5555

class Client:
    def __init__(self,ip,port):
        self.ip = ip
        self.port = port
        self.address = (self.ip,self.port)
        self.client = socket.socket(socket.AF_INET,socket.soCK_STREAM)

    def server(self):
        try:
            self.client.connect(self.address)
            print(f"Successfully connected to {self.address}")
        except:
            pass

    def send_message(self,message):
        self.client.sendall(pickle.dumps(message))

    def receive_message(self):
        return pickle.loads(self.client.recv(8192*4))

这是我在这里使用的 pygame_textinput 库。

解决方法

您的第三个问题(截断的pickle 数据)是因为您使用的是TCP,并且您正在解开recv 返回的任何内容。您可能会想,每当您 send 某事时,接收方调用 recv 时,都会返回完全相同的内容,但实际上并非如此。 TCP 将您的数据拆分成数据包,因此接收方可能不会同时收到所有数据。

例如,如果您发送“abcdefgh”,然后单独发送“ijkl”,则允许第一次接收返回“abcd”,第二次接收返回“efghijkl”。或者第一个可以返回“ab”,第二个可以返回“cde”,第三个可以返回“fghijkl”,依此类推。

你必须设计一种方法让接收者知道什么时候停止接收。例如,如果您先发送“8abcdefgh”,然后发送“4ijkl”,则接收方可能会收到“8abcdefgh4ij”,然后它知道“8abcdefgh”是一个“发送”(因为它以 8 个字节开始,然后再发送 8 个字节)并且它知道“4ij”是下一个“发送”的开始,但不是全部(因为它以 4 开头,但没有 4 个字节)。

另一种方法是在每条消息后发送一个特殊字符,如换行符(回车键)。这可能不适用于泡菜,因为泡菜中可以有换行符。但是您可以选择泡菜没有的另一个字节,例如 0xFF。然后接收方知道继续接收,直到它看到字节 0xFF。

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