斩波在线蟒蛇游戏



我已经使用pygame和sockets在python中编程了Slither.io的简单克隆,我有三个问题:

  1. 当我一个人在笔记本电脑上玩游戏时,游戏是断断续续的。每隔十秒钟,我的游戏就会停滞一段时间(1毫秒(,然后继续。这不是什么大问题,但很烦人
  2. 当我在本地网络的两台电脑上玩时,我看到另一个玩家(另一条蛇(也在震荡
  3. 最奇怪的问题是,当我在主笔记本电脑上运行服务器,然后在第二台笔记本电脑上执行游戏时,游戏开始,几秒钟后崩溃。客户端上的调试器表示,pickle数据在从服务器接收数据时被截断。但当我在第二台笔记本电脑上运行服务器程序,在主笔记本电脑上玩游戏时,一切都还好。为什么

我尝试过:
问题1。更改客户端上的FPS和服务器上的time.sleep
问题2。change 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), (0, 80, 0), (0, 0, 140), (80, 80, 0), (80, 0, 80), (0, 80, 80)]
food = []
for i in range(0, 300, 1):
food.append([random.randint(10, 3290), random.randint(10, 2090), (0, 0, 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, addr, 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, 0, 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, len(snakes), 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, 3290), random.randint(10, 2090), (0, 0, 100), 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, ConnectionResetError, 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, y, color, length, diameter, name, 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, x, y, diameter, step, circle):
self.x = x
self.y = y
self.color = (80, 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, x, y, color, diameter, 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], [i - snake.x + 550, 2100 - snake.y + 350], 1)
for i in range(0, 2101, 100):
pygame.draw.line(win, (60, 60, 60), [0 - snake.x + 550, i - snake.y + 350], [3300 - snake.x + 550, i - snake.y + 350], 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, 0, 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.y, snake.diameter, snake.step, snake)
segments.append(segment)
for i in range(0, snake.length - 2, 1):
segment = Segment(snake.x, snake.y, snake.diameter, snake.step, 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, len(other_snakes), 1):
snakes.append({"name": other_snakes[i].name, "score": other_snakes[i].score})
win.fill((100, 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, len(other_segments), 1):
other_segments[i].draw()
for i in range(0, len(other_snakes), 1):
other_snakes[i].draw()
name = font.render(other_snakes[i].name, True, (0, 0, 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, len(segments), 1):
segments[i].move()
for i in range(0, len(segments), 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, snake.y, snake.diameter, snake.step, 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, True, (0, 0, 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, len(snakes), 1):
table = font.render(str(snakes[i]["name"]), True, (0, 0, 0))
win.blit(table, (900, 50 + i*30))
for i in range(0, len(snakes), 1):
table = font.render(str(snakes[i]["score"]), True, (0, 0, 0))
win.blit(table, (1050, 50 + i*30))
for i in range(0, len(snakes), 1):
table = font.render(str(i + 1) + ".", True, (0, 0, 0))
win.blit(table, (870, 50 + i*30))
pygame.display.update()
clock.tick(140)
return_snakes()
#create food from head of dead snake
ds = [snake.x, snake.y, snake.color, 15, 5]
dead_snake.append(ds)
#create food from segments of dead snake
for i in range(0, len(segments), 1):
ds = [segments[i].x, segments[i].y, snake.color, 15, 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][1], received[0][2], received[0][3], received[0][4], received[0][5], received[0][6], received[0][7])
segments = []
segment = Segment(snake.x, snake.y, snake.diameter, snake.step, snake)
segments.append(segment)
for i in range(0, snake.length - 2, 1):
segment = Segment(snake.x, snake.y, snake.diameter, snake.step, segment)
segments.append(segment)
break
win.fill((100, 100, 100))
screen()
#press space bar to continue
cont = big_font.render("Press space bar to continue", True, (0, 0, 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,并且您正在取消picklerecv返回的任何内容。您可能会认为,每当您send某个东西,并且接收器调用recv时,都会返回完全相同的东西,但实际上并没有。TCP将您的数据拆分为数据包,因此接收器可能无法同时接收所有数据。

例如,如果您发送";abcdefgh";然后分别发送";ijkl";,允许第一个接收返回"0";abcd";第二个返回"0";efghijkl";。或者第一个可以返回";ab";第二个可以返回"0";cde";第三个可以返回"0";fghijkl";,等等

你必须设计一种方法让接收器知道何时停止接收。例如,如果您发送";8abcdefgh";然后";4ijkl";,接收机可以得到";8abcdefgh4ij";,然后它知道";8abcdefgh";是一个";发送";(因为它从8个字节开始,然后再增加8个字节(;4ij";是下一个";发送";但这并不是全部(因为它以4开始,但没有更多的4个字节(。

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

最新更新