Python3.7版---双人联机雷霆战机(2D特效+音效+道具+Linux系统)
                                                            生活随笔
收集整理的這篇文章主要介紹了
                                Python3.7版---双人联机雷霆战机(2D特效+音效+道具+Linux系统)
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.                        
                                作品原名稱:MultiplayerGameEngine-master
作者:Tomcat(好像是這個名字沒注意…)
改良者:CSDN阿坤
Hello everyone!
 Today ,I will to be give everyone show a game!(英語-2級水平)
 今天為大家?guī)淼氖且豢铍p人游戲并且是2D喲,并且還附帶音效道具等等,游戲環(huán)境只需要Python3以及pygame&&numpy即可完美運行,是小編從pygame上挑的一款比較不錯的游戲,初始解包后直接運行雖然各種報錯但是經(jīng)過小編不辭辛苦的努力終于將其在Linux系統(tǒng)下完美運行!
 因為是一款雙人游戲,所以游戲的主文件分為兩個 分別是Player1、Player2
由于兩者內(nèi)容差不多所以呢在這里只展示Player1完整代碼:
#!/usr/bin/env python3 # -*- coding:utf-8 -*-import osfrom pygame import freetype__author__ = "Yoann Berenguer" __credits__ = ["Yoann Berenguer"] __version__ = "1.0.0" __maintainer__ = "Yoann Berenguer" __email__ = "yoyoberenguer@hotmail.com"import random import socket import _pickle as cpickle import threading import time import copyregtry:import pygame except ImportError:print("\n<Pygame> library is missing on your system.""\nTry: \n C:/pip install pygame on a window command prompt.")raise SystemExittry:import lz4.frame except ImportError:print("\n<lz4> library is missing on your system.""\nTry: \n C:/pip install lz4 on a window command prompt.")raise SystemExittry:from CreateHalo import PlayerHalofrom SoundServer import SoundControlfrom TextureTools import *from NetworkBroadcast import Broadcast, EventAttr, StaticSprite, AnimatedSprite, SoundAttr, BlendSpritefrom Explosions import Explosionfrom LayerModifiedClass import LayeredUpdatesModifiedimport GLOBALfrom GLOBAL import GLfrom ShootingStars import ShootingStarfrom AfterBurners import AfterBurnerfrom End import PlayerLostfrom PlayerScore import DisplayScorefrom CosmicDust import COSMIC_DUST_ARRAY, create_dust, display_dustfrom Gems import MakeGemsfrom LifeBar import HorizontalBarexcept ImportError:print("\nOne or more game libraries is missing on your system.""\nDownload the source code from:\n""https://blog.csdn.net/qq_43417559")raise SystemExit# socket.setdefaulttimeout(0)def unserialize_event(is_set: threading.Event) -> threading.Event:"""Set the internal flag to true. All threads waiting for it to become true are awakened.Return a threading event set to True:param is_set: threading event:return: return a threading event set to true.>>> event_ = threading.Event()>>> u_event = unserialize_event(event_)>>> assert isinstance(u_event, threading.Event)>>> event_.set()>>> event_.isSet()True>>> u_event = unserialize_event(event_)>>> u_event.isSet()True>>> event_.clear()>>> u_event = unserialize_event(event_)>>> u_event.isSet()True"""assert isinstance(is_set, threading.Event), \print("Positional argument <is_set> is type %s , expecting threading.Event." % type(is_set))event_ = threading.Event()if is_set:event_.set()return event_def serialize_event(e: threading.Event) -> tuple:""":param e: threading event:return: <function unserialize_event>, True or False>>> event_ = threading.Event()>>> s = serialize_event(event_)>>> assert isinstance(s, tuple)>>> u, v = list(s)>>> assert isinstance(u, type(unserialize_event))>>> assert v[0] == False>>> event_ = threading.Event()>>> event_.set()>>> s = serialize_event(event_)>>> assert isinstance(s, tuple)>>> u, v = list(s)>>> assert isinstance(u, type(unserialize_event))>>> assert v[0] == True"""assert isinstance(e, threading.Event), \print("Positional argument <e> is type %s , expecting threading.Event." % type(e))return unserialize_event, (e.isSet(),)copyreg.pickle(threading.Event, serialize_event)class LaserImpact(pygame.sprite.Sprite):containers = Noneimages = Nonedef __init__(self, gl_, pos_, parent_, timing_=16, blend_=None, layer_=0):"""Create an impact sprite effect (absorption effect) where the laser is colliding.:param gl_: class GL (contains all the game global variables):param pos_: tuple of the impact position (x:int, y:int):param parent_: parent object (MirroredPlayer1Class class instance):param timing_: integer; refreshing time in milliseconds (default 16ms is 60FPS):param blend_: integer; blend effect to apply to the sprite, default pygame.BLEND_RGB_ADD = 0:param layer_: integer < 0; represent the sprite layer. default = 0"""assert isinstance(pos_, tuple), \"Positional argument <pos_> is type %s , expecting tuple." % type(pos_)assert isinstance(parent_, Asteroid), \"Positional argument <parent_> is type %s , expecting class MirroredPlayer1Class instance." % type(parent_)assert isinstance(timing_, int), \"Positional argument <timing_> is type %s , expecting integer." % type(timing_)if blend_ is None:raise ValueError('Blend should not be unset!')else:assert isinstance(blend_, int), \"Positional argument <blend_> is type %s , expecting integer." % type(blend_)assert isinstance(layer_, int), \"Positional argument <layer_> is type %s , expecting integer." % type(layer_)if self.containers is None:raise ValueError('LaserImpact.containers is not initialised.\nMake sure to assign the containers to'' a pygame group prior instantiation.\ne.g: LaserImpact.containers = ''pygame.sprite.Group()')if self.images is None:raise ValueError("LaserImpact.images is not initialised.\nMake sure to assign a texture to ""prior instantiation.\ne.g: LaserImpact.images = 'P1_SURFACE'")if timing_ < 0:raise ValueError('Positional argument timing_ cannot be < 0')pygame.sprite.Sprite.__init__(self, self.containers)if isinstance(self.containers, pygame.sprite.LayeredUpdates):if layer_:self.containers.change_layer(self, layer_)assert isinstance(self.images, (list, pygame.Surface))self.image = self.images[0] if isinstance(self.images, list) else self.imagesself.rect = self.image.get_rect(center=pos_)self.timing = timing_self.dt = 0self.index = 0self.gl = gl_self.blend = blend_self.layer = layer_self.length = len(self.images) - 1self.parent_ = parent_self.surface_name = 'IMPACT_LASER'self.id_ = id(self)self.impact_object = Broadcast(self.make_object())def make_object(self) -> AnimatedSprite:"""Create an AnimatedSprite message object (see NetworkBroadcast library):return: AnimatedSprite instance"""# Only attributes self.gl.FRAME change, self.rect and self.index are changing over the time.return AnimatedSprite(frame_=self.gl.FRAME, id_=self.id_, surface_=self.surface_name,layer_=self.layer, blend_=self.blend, rect_=self.rect,index_=self.index)def update(self):if self.dt > self.timing:if self.rect.colliderect(self.gl.SCREENRECT):if isinstance(self.images, list):self.image = self.images[self.index % self.length]# follow the parent object with half of its speedself.rect.move_ip(self.parent_.speed // 2)self.index += 1if self.index > self.length:self.kill()self.dt = 0else:self.kill()if self.rect.colliderect(self.gl.SCREENRECT):self.impact_object.update({'frame': self.gl.FRAME, 'rect': self.rect, 'index': self.index})self.impact_object.queue()self.dt += self.gl.TIME_PASSED_SECONDSclass Shot(pygame.sprite.Sprite):images = Nonecontainers = Nonelast_shot = 0shooting = Falsedef __init__(self, parent_, pos_, gl_, timing_=0, layer_=-1, surface_name_=''):"""Create a sprite shoot:param parent_: parent object class Player:param pos_: tuple (x:int, y:int); sprite shoot position (start position):param gl_: Constants (class GL):param timing_: integer; sprite refreshing time FPS, e.g 16ms is 60FPS:param layer_: integer; sprite layer (default 0 top layer):param surface_name_: string; surface name e.g 'BLUE_LASER'; surface = eval(BLUE_LASER')"""assert isinstance(parent_, Player1), \"Positional argument <parent_> is type %s , expecting class MirroredPlayer1Class instance." % type(parent_)assert isinstance(pos_, tuple), \"Positional argument <pos_> is type %s , expecting tuple." % type(pos_)assert isinstance(timing_, int), \"Positional argument <timing_> is type %s , expecting integer." % type(timing_)assert isinstance(layer_, int), \"Positional argument <layer_> is type %s , expecting integer." % type(layer_)assert isinstance(surface_name_, str), \"Positional argument <surface_name_> is type %s , " \"expecting python string." % type(surface_name_)if self.containers is None:raise ValueError('Shot.containers is not initialised.\nMake sure to assign the containers to'' a pygame group prior instantiation.\ne.g: Shot.containers = pygame.sprite.Group()')if self.images is None:raise ValueError("Shot.images is not initialised.\nMake sure to assign a texture to ""prior instantiation.\ne.g: Shot.images = 'P1_SURFACE'")if timing_ < 0:raise ValueError('Positional argument timing_ cannot be < 0')self.layer = layer_pygame.sprite.Sprite.__init__(self, self.containers)if isinstance(gl_.All, pygame.sprite.LayeredUpdates):if layer_:gl_.All.change_layer(self, layer_)self.images = Shot.imagesself.image = self.images[0] if isinstance(self.images, list) else self.imagesself.speed = pygame.math.Vector2(0, -35)self.timing = timing_self.pos = pos_self.gl = gl_self.position = pygame.math.Vector2(*self.pos)self.rect = self.image.get_rect(center=self.pos)self.dt = 0self.blend = pygame.BLEND_RGBA_ADD# todo test if convert_alpha otherwise this is uselessself.mask = pygame.mask.from_surface(self.image) # Image have to be convert_alpha compatibleself.index = 0self.parent = parent_self.surface_name = surface_name_self.id_ = id(self)if Shot.shooting and self.is_reloading(self.gl.FRAME):self.kill()else:self.gl.MIXER.stop_object(id(BLUE_LASER_SOUND))self.gl.MIXER.play(sound_=BLUE_LASER_SOUND, loop_=False, priority_=0, volume_=1.0,fade_out_ms=0, panning_=True, name_='BLUE_LASER_SOUND', x_=self.rect.centerx,object_id_=id(BLUE_LASER_SOUND), screenrect_=self.gl.SCREENRECT)self.sound_object = Broadcast(self.make_sound_object('BLUE_LASER_SOUND'))self.sound_object.play()Shot.last_shot = FRAMEShot.shooting = True# Create a network object self.shot_object = Broadcast(self.make_object())self.shot_object.queue()def make_sound_object(self, sound_name_: str) -> SoundAttr:"""Create a sound object for network broadcasting.:param sound_name_: string; representing the texture to use e.g 'BLUE_LASER_SOUND":return: Sound object, SoundAttr instance."""assert isinstance(sound_name_, str), \"Positional argument <sound_name_> is type %s , expecting python string." % type(sound_name_)if sound_name_ not in globals():raise NameError('Sound %s is not define.' % sound_name_)return SoundAttr(frame_=self.gl.FRAME, id_=self.id_, sound_name_=sound_name_, rect_=self.rect)def make_object(self) -> StaticSprite:"""Create a StaticSprite message object (see NetworkBroadcast library):return: StaticSprite object"""# Only attributes self.gl.FRAME change and self.rect are changing over the time.return StaticSprite(frame_=self.gl.FRAME, id_=self.id_, surface_=self.surface_name,layer_=self.layer, blend_=self.blend, rect_=self.rect)@staticmethoddef is_reloading(frame_: int) -> bool:"""Check if the player is shooting or reloading.Compare the actual FRAME number to the latest Shot frame number.Shot.last_shot default value is set to 0 during instantiation.Reloading time is hard encoded to 10 frames interval.When the player is ready to shoot, the shooting flag is set to false, otherwise stays True:frame_: integer; must be > 0 (Actual frame number):return: True or False. True player is reloading, False player is ready to shoot>>> Shot.shooting = True>>> Shot.last_shot = 0>>> Shot.is_reloading(9)True>>> assert Shot.shooting is True>>> Shot.is_reloading(10)False>>> Shot.is_reloading(-1)Traceback (most recent call last):...ValueError: frame_ must be >= 0"""assert isinstance(frame_, int), \"argument frame_ should be integer got %s" % type(frame_)if not frame_ >= 0:raise ValueError("frame_ must be >= 0")assert hasattr(Shot, 'last_shot'), 'Class Shot is missing attribute <last_shot>.'assert hasattr(Shot, 'shooting'), 'Class Shot is missing attribute <shooting>.'assert isinstance(Shot.last_shot, int), \"last_shot variable should be integer got %s" % type(Shot.last_shot)assert frame_ >= Shot.last_shot, \"Game constant frame_ value:%s should be > or equal to %s " % (frame_, Shot.last_shot)if frame_ - Shot.last_shot < 10:# still reloadingreturn Trueelse:# ready to shootShot.shooting = Falsereturn Falsedef collide(self, rect_: pygame.Rect, object_) -> None:"""Create a laser impact sprite:param rect_: pygqme.Rect object:param object_: object colliding with the rectangle:return: None"""assert isinstance(rect_, pygame.Rect), \'Positional argument rect_ should be a <pygame.Rect> type got %s ' % type(rect_)assert object_ is not None, 'Positional argument object_ cannot be None'# if sprite belongs to any group(s)if self.alive():LaserImpact.containers = self.gl.AllLaserImpact.images = IMPACT_LASERLaserImpact(gl_=self.gl, pos_=rect_.topleft, parent_=object_,timing_=self.timing, blend_=pygame.BLEND_RGBA_ADD, layer_=0)self.kill()def update(self) -> None:"""Update shot sprites whereabouts.sprite position is given by its rectangle.The position changed by incrementing the position with its speed vector (self.speed)if the sprite belongs to the screen dimensions, a message is broadcast to the client:return: None"""if self.dt > self.timing:if self.gl.SCREENRECT.colliderect(self.rect):# Move the laser spriteif self.images != IMPACT_LASER:self.position += self.speedself.rect.center = (self.position.x, self.position.y)self.dt = 0 else:self.kill()# broadcast network messageif self.rect.colliderect(self.gl.SCREENRECT):self.shot_object.update({'frame': self.gl.FRAME, 'rect': self.rect})self.shot_object.queue()self.dt += self.gl.TIME_PASSED_SECONDSclass Player1(pygame.sprite.Sprite):containers = Noneimage = Nonedef __init__(self, gl_, timing_=15, pos_: tuple = (0, 0), layer_=0):""":param gl_: Game constants (GL class) see GLOBAL library for more details:param timing_: integer; default 15ms (60 FPS):param pos_: tuple; (x:int, y:int) representing player 1 position (default x=0, y=0):param layer_: Sprite layer used by player 1"""assert isinstance(timing_, int), \"Positional argument <timing_> is type %s , expecting integer." % type(timing_)assert isinstance(pos_, tuple), \"Positional argument <pos_> is type %s , expecting tuple." % type(pos_)assert isinstance(layer_, int), \"Positional argument <layer_> is type %s , expecting integer." % type(layer_)if self.containers is None:raise ValueError('MirroredPlayer1Class.containers is not initialised.\nMake sure to assign the containers to'' a pygame group prior instantiation.\ne.g: MirroredPlayer1Class.containers = pygame.sprite.Group()')if self.image is None:raise ValueError("MirroredPlayer1Class.image is not initialised.\nMake sure to assign a texture to ""prior instantiation.\ne.g: MirroredPlayer1Class.image = 'P1_SURFACE'")pygame.sprite.Sprite.__init__(self, self.containers)assert isinstance(Player1.image, (pygame.Surface, list)), \"image is not a pygame.Surface or a list, got %s instead" % type(Player1.image)if timing_ < 0:raise ValueError('Positional argument timing_ cannot be < 0')self.image = Player1.imageself.image_copy = self.image.copy()self.rect = self.image.get_rect(center=pos_)self.timing = timing_self.surface_name = 'P1_SURFACE'self.gl = gl_self.dt = 0self.speed = 300self.layer = layer_self.blend = 0self.shooting = Falseself.previous_pos = pygame.math.Vector2() # previous positionself.life = 200 # player's lifeself.max_life = 200 # maximum lifeself.eng_right = self.right_engine() # instance for right engineself.eng_left = self.left_engine() # isntance for left engine# todo test if convert_alpha otherwise this is uselessself.mask = pygame.mask.from_surface(self.image) # Image have to be convert_alpha compatibleself.damage = 800 # -> gives 800 hit point of damage after collisionself.id_ = id(self)self.player_object = Broadcast(self.make_object())self.impact_sound_object = Broadcast(self.make_sound_object('IMPACT'))self.update_score = self.gl.P1_SCORE.score_updatedef make_sound_object(self, sound_name_: str) -> SoundAttr:"""Create a sound object for network broadcasting.:param sound_name_: string; representing the texture to use e.g 'BLUE_LASER_SOUND":return: Sound object, SoundAttr instance."""assert isinstance(sound_name_, str), \'Positional argument sound_name_ is not a string type, got %s ' % type(sound_name_)if sound_name_ not in globals():raise NameError('Sound %s is not define.' % sound_name_)return SoundAttr(frame_=self.gl.FRAME, id_=self.id_, sound_name_=sound_name_, rect_=self.rect)def make_object(self) -> StaticSprite:"""Create a sprite object for network broadcast similar to MirroredPlayer1Class:return: StaticSprite object (see NetworkBroadcast library for more details)"""# Only attributes self.gl.FRAME, self.rect are changing over the time.return StaticSprite(frame_=self.gl.FRAME, id_=self.id_, surface_=self.surface_name,layer_=self.layer, blend_=self.blend, rect_=self.rect, life=self.life, damage=self.damage)def player_lost(self):PlayerLost.containers = self.gl.AllPlayerLost.DIALOGBOX_READOUT_RED = DIALOGBOX_READOUT_REDPlayerLost.SKULL = SKULLfont = freetype.Font('/home/student/桌面/MultiplayerGameEngine-master/Assets/Fonts/Gtek Technology.ttf', size=14)PlayerLost(gl_=self.gl, font_=font, image_=FINAL_MISSION, layer_=0)def explode(self) -> None:"""Player explosion sprites and halo:return: None"""if self.alive():Explosion.images = EXPLOSION2Explosion(self, self.rect.center,self.gl, self.timing, self.layer, texture_name_='EXPLOSION2')PlayerHalo.images = HALO_SPRITE13PlayerHalo.containers = self.gl.AllPlayerHalo(texture_name_='HALO_SPRITE13', object_=self, timing_=10)self.player_lost()self.kill()def collide(self, damage_: int) -> None:"""MirroredPlayer1Class collide with object, transfer the damage and play the collision sound locallyif life < 1, trigger player1 explosion.:param damage_: integer; must be > 0 (total damage transferred to the player after collision.):return: None"""assert isinstance(damage_, int), \'Positional argument damage_ is not an int, got %s instead.' % type(damage_)assert damage_ > 0, 'damage_ argument should be > 0 'print(self.life, damage_, self.life - damage_)if self.alive():self.life -= damage_self.gl.MIXER.play(sound_=IMPACT, loop_=False, priority_=0,volume_=1.0, fade_out_ms=0, panning_=True,name_='IMPACT', x_=self.rect.centerx,object_id_=id(IMPACT),screenrect_=self.gl.SCREENRECT) self.impact_sound_object.play()"""def hit(self, damage_: int) -> None:#Transfer damage to the player after being hit.#:param damage_: integer > 0, damage transfer to the player#:return: Noneassert isinstance(damage_, int), \'Positional argument damage_ is not an int, got %s instead.' % type(damage_)assert damage_ > 0, 'damage_ argument should be > 0 'if self.alive():self.life -= damage_"""def left_engine(self) -> AfterBurner:"""Create a sprite for the left engine:return: AfterBurner instance"""if EXHAUST:AfterBurner.images = EXHAUSTelse:raise NameError('EXHAUST is not defined.')return AfterBurner(self, self.gl, (-22, 38),self.timing, pygame.BLEND_RGB_ADD, self.layer - 1, texture_name_='EXHAUST')def right_engine(self) -> AfterBurner:"""Create a sprite for the right engine:return: AfterBurner instance"""if EXHAUST:AfterBurner.images = EXHAUSTelse:raise NameError('EXHAUST is not defined.')return AfterBurner(self, self.gl, (22, 38),self.timing, pygame.BLEND_RGB_ADD, self.layer - 1, texture_name_='EXHAUST')def get_centre(self) -> tuple:"""Get MirroredPlayer1Class position.:return: tuple representing MirroredPlayer1Class rect centre"""return self.rect.centerdef disruption(self) -> None:"""Create an electric effect on MirroredPlayer1Class hull.:return: None"""if 'DISRUPTION' in globals():if isinstance(DISRUPTION, list):index = (FRAME >> 1) % len(DISRUPTION) - 1else:raise ValueError('DISRUPTION is not a list, got %s instead.' % type(DISRUPTION))else:raise NameError('DISRUPTION is not defined.')self.image.blit(DISRUPTION[index], (-20, -20), special_flags=pygame.BLEND_RGB_ADD)# self.disruption_object.update({'frame': self.gl.FRAME, 'surface': 'DISRUPTION',# 'rect': self.rect, 'index': index})# self.disruption_object.show(self.disruption_object)# self.disruption_object.queue()def shooting_effect(self) -> pygame.Surface:"""Apply a special effect on the aircraft hull when firing.:return: pygame.Surface"""if 'GRID' in globals():self.image.blit(GRID, (0, 0), special_flags=pygame.BLEND_RGB_ADD)else:raise NameError('GRID is not defined.')return self.imagedef update(self) -> None:"""Update MirroredPlayer1Class sprite:return: None"""self.rect.clamp_ip(self.gl.SCREENRECT)if self.dt > self.timing:self.image = self.image_copy.copy()if self.life < 1:self.explode()if self.gl.KEYS[pygame.K_UP]:self.rect.move_ip(0, -self.speed * self.gl.SPEED_FACTOR)if self.gl.KEYS[pygame.K_DOWN]:self.rect.move_ip(0, +self.speed * self.gl.SPEED_FACTOR)if self.gl.KEYS[pygame.K_LEFT]:self.rect.move_ip(-self.speed * self.gl.SPEED_FACTOR, 0)if self.gl.KEYS[pygame.K_RIGHT]:self.rect.move_ip(+self.speed * self.gl.SPEED_FACTOR, 0)# if self.gl.JOYSTICK is not None and self.gl.JOYSTICK.PRESENT:# x, y = self.gl.JOYSTICK.axes_status[0]if self.gl.KEYS[pygame.K_SPACE]:self.shooting_effect()Shot(self, self.rect.center, self.gl, 0, self.layer - 1, surface_name_='BLUE_LASER')if joystick is not None:self.rect.move_ip(JL3.x * self.gl.SPEED_FACTOR * self.speed,JL3.y * self.gl.SPEED_FACTOR * self.speed)if self.alive():# Broadcast the spaceship position every framesself.player_object.update({'frame': self.gl.FRAME,'rect': self.rect,'life': self.life})self.player_object.queue()if self.previous_pos == self.rect.center:self.rect.centerx += random.randint(-1, 1)self.rect.centery += random.randint(-1, 1)self.previous_pos = self.rect.center# !UPDATE the <follower> sprites with the new player position.self.eng_left.update()self.eng_right.update()else:self.dt += self.gl.TIME_PASSED_SECONDSself.disruption()class MirroredPlayer2Class(pygame.sprite.Sprite):def __init__(self, sprite_):"""Create an instance of Player2 on the server:param sprite_: object containing all attributes>>> import pygame>>> pygame.init()(8, 0)>>> SCREENRECT = pygame.Rect(0, 0, 800, 1024)>>> screen = pygame.display.set_mode(SCREENRECT.size, pygame.HWSURFACE, 32)>>> from Textures import P1_SURFACE, DISRUPTION>>> attributes = {'rect': pygame.Rect(0, 0, 0, 0),... 'image':eval('P1_SURFACE'), 'blend':0, 'layer':-1, 'id_':35555,... 'frame':0, 'damage': 800, 'life': 200, 'surface':'P1_SURFACE'}>>> sprite_ = pygame.sprite.Sprite()>>> for attr, value in attributes.items():... setattr(sprite_, attr, value)>>> spr = MirroredPlayer2Class(sprite_)>>> print(spr.surface)P1_SURFACE"""assert sprite_ is not None, 'Positional argument sprite_ is None.'attributes = ['rect', 'image', 'blend', 'layer', 'id_', 'frame', 'damage', 'life', 'surface']for attr in attributes:assert hasattr(sprite_, attr),\'Positional argument sprite_ is missing attribute %s ' % attrpygame.sprite.Sprite.__init__(self)self.rect = sprite_.rectself.image = sprite_.imageself.image_copy = sprite_.image.copy()self.blend = sprite_.blendself.layer = sprite_.layerself.id_ = sprite_.id_self.frame = sprite_.frameself.surface = sprite_.surfaceself.damage = sprite_.damageself.gl = GLdef disruption(self) -> None:if globals().__contains__('FRAME') and globals().__contains__('DISRUPTION'):index = (FRAME >> 1) % len(DISRUPTION) - 1self.image.blit(DISRUPTION[index], (-20, -20), special_flags=pygame.BLEND_RGB_ADD)def update(self) -> None:if self.image is None:raise ValueError('Cannot copy() NoneType.')self.image = self.image_copy.copy()self.disruption()class P2Shot(pygame.sprite.Sprite):def __init__(self, gl_, sprite_, timing_=0):""":param gl_: class GL (Constants):param sprite_: object containing all the original sprite attributes:param timing_: integer > 0 representing the refreshing time."""assert isinstance(gl_, type(GL)), \"Positional argument <gl_> is type %s , expecting class GL instance." % type(gl_)assert sprite_ is not None, 'Positional argument sprite_ is None.'attributes = ['rect', 'image', 'blend', 'layer', 'id_', 'surface']for attr in attributes:assert hasattr(sprite_, attr), \'Positional argument sprite_ is missing attribute %s ' % attrif timing_ < 0:raise ValueError('argument timing_ must be > 0')pygame.sprite.Sprite.__init__(self)self.rect = sprite_.rectself.image = sprite_.imageself.blend = sprite_.blendself.layer = sprite_.layerself.id_ = sprite_.id_self.surface = sprite_.surfaceself.gl = gl_self.timing = timing_def collide(self, rect_, object_) -> None:""":param rect_: pygame.Rect type:param object_::return:"""if hasattr(GL, 'All'):LaserImpact.containers = GL.Allelse:raise AttributeError('Class GL missing attribute All.')if IMPACT_LASER:LaserImpact.images = IMPACT_LASERelse:raise NameError('IMPACT_LASER is not define.')LaserImpact(gl_=self.gl, pos_=rect_.topleft, parent_=object_,timing_=self.timing, blend_=pygame.BLEND_RGBA_ADD, layer_=0)def update(self) -> None:...class SpriteServer(threading.Thread):def __init__(self,gl_, # Global variables classhost_: str, # host address (string)port_: int, # port value (integer)):""":param gl_: class GL:param host_: string; ip address:param port_: integer; port to use"""assert isinstance(host_, str), \"Positional argument <host_> is type %s , expecting string." % type(host_)assert isinstance(port_, int), \"Positional argument <port_> is type %s , expecting integer" % type(port_)threading.Thread.__init__(self)self.gl = gl_self.gl.SPRITE_SERVER_STOP = Falsetry:self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)except socket.error as error:print('\n[-]SpriteServer - ERROR : %s %s' % (error, time.ctime()))gl_.P1CS_STOP = Trueself.gl.SPRITE_SERVER_STOP = Truetry:self.sock.bind((host_, port_))self.sock.listen(1)except socket.error as error:print('\n[-]SpriteServer - ERROR : %s %s' % (error, time.ctime()))gl_.P1CS_STOP = Trueself.gl.SPRITE_SERVER_STOP = Trueself.buf = self.gl.BUFFERself.total_bytes = 0self.view = memoryview(bytearray(self.buf))def run(self):# Accept a connection. The socket must be bound to# an address and listening for connections.# The return value is a pair (conn, address) where# conn is a new socket object usable to send and receive# data on the connection, and address is the address# bound to the socket on the other end of the connection.connection = Nonetry:# The thread will be stopped here until first connectionconnection, client_address = self.sock.accept()except socket.error as error:print("\n[-]SpriteServer - Lost connection with Player 2 ...")print("\n[-]SpriteServer - ERROR %s %s" % (error, time.ctime()))self.gl.P1CS_STOP = Truewhile not self.gl.P1CS_STOP and not self.gl.SPRITE_SERVER_STOP:# try:while not self.gl.P1CS_STOP and not self.gl.SPRITE_SERVER_STOP:# Receive data from the socket, writing it into buffer instead# of creating a new string. The return value is a pair (nbytes, address)# where nbytes is the number of bytes received and address is the address# of the socket sending the data.try:nbytes, sender = connection.recvfrom_into(self.view, self.buf)except socket.error as error:print("\n[-]SpriteServer - Lost connection with Player 2 ...")print("\n[-]SpriteServer - ERROR %s %s" % (error, time.ctime()))# signal to kill both threads SpriteServer and SpriteClient# todo : Player 2 is now deconnected from the server and should not be# display on the server display, create a method to kill the sprite of Player 2self.gl.SPRITE_SERVER_STOP = Truenbytes = 0buffer = self.view.tobytes()[:nbytes]try:connection.sendall(self.view.tobytes()[:nbytes])except ConnectionResetError as error:print("\n[-]SpriteServer - Lost connection with Player 2 ...")print("\n[-]SpriteServer - ERROR %s %s" % (error, time.ctime()))# todo : Player 2 is now deconnected from the server and should not be# display on the server display, create a method to kill the sprite of Player 2# signal to kill both threads SpriteServer and SpriteClientself.gl.SPRITE_SERVER_STOP = Truetry:# Decompress the data framedecompress_data = lz4.frame.decompress(buffer)data = cpickle.loads(decompress_data)except Exception:# The decompression error can also happen when# the bytes stream sent is larger than the buffer size.# todo : Player 2 is now deconnected from the server and should not be# display on the server display, create a method to kill the sprite of Player 2# signal to kill both threads SpriteServer and SpriteClientself.gl.SPRITE_SERVER_STOP = Trueself.gl.SPRITE_CLIENT_STOP = Truedata = None# todo check if self.gl.NetGroupAll.empty() is faster# self.gl.NetGroupAll = LayeredUpdatesModified()data_set = set()if isinstance(data, list):for sprite_ in data:# print(GL.FRAME, sprite_.id_, sprite_.surface if hasattr(sprite_, "surface") else None)if hasattr(sprite_, 'event'):continueelif hasattr(sprite_, 'sound_name'):try:sound = eval(sprite_.sound_name)except NameError:raise NameError("\n[-]SpriteServer - Sound effect ""'%s' does not exist " % sprite_.sound_name)# self.gl.MIXER.stop_object(id(sound))# play the sound locallyself.gl.MIXER.play(sound_=sound, loop_=False, priority_=0,volume_=1.0, fade_out_ms=0, panning_=True,name_=sprite_.sound_name, x_=sprite_.rect.centerx,object_id_=id(sound),screenrect_=self.gl.SCREENRECT)# data.remove(sprite_)else:assert hasattr(sprite_, 'surface'), "\nBroadcast message is missing <surface> attribute."try:sprite_.image = eval(sprite_.surface) # load surfaceexcept (NameError, AttributeError):raise RuntimeError("\n[-]SpriteServer - Surface ""'%s' does not exist " % sprite_.surface)if isinstance(sprite_.image, list):sprite_.image = sprite_.image[sprite_.index % len(sprite_.image) - 1]# --- Apply transformation ---# Apply transformation to texture rotation/scale and# store the transformation inside a buffer# Check if the texture has been already transformed and use# the buffer transformation instead (for best performance).if hasattr(sprite_, 'rotation'):if sprite_.rotation is not None and sprite_.rotation != 0:if sprite_.id_ in self.gl.XTRANS_ROTATION.keys():sprite_.image = self.gl.XTRANS_ROTATION[sprite_.id_]else:sprite_.image = pygame.transform.rotate(sprite_.image, sprite_.rotation)self.gl.XTRANS_ROTATION.update({sprite_.id_: sprite_.image})if hasattr(sprite_, 'scale'):if sprite_.scale != 1:if sprite_.id_ in self.gl.XTRANS_SCALE.keys():sprite_.image = self.gl.XTRANS_SCALE[sprite_.id_]else:sprite_.image = pygame.transform.scale(sprite_.image, (int(sprite_.image.get_size()[0] * sprite_.scale),int(sprite_.image.get_size()[1] * sprite_.scale)))self.gl.XTRANS_SCALE.update({sprite_.id_: sprite_.image})s = None# find Player 2if sprite_.surface == 'P2_SURFACE':s = MirroredPlayer2Class(sprite_)# find player 2 shotselif sprite_.surface == "RED_LASER":s = P2Shot(self.gl, sprite_, 16)# generic sprite that doesn't have# to be instantiated with specific methodselse:# Generic sprite (without methods)s = pygame.sprite.Sprite()s.frame = sprite_.frames.rect = sprite_.rects.surface = sprite_.surfaces.image = sprite_.images.blend = sprite_.blends.layer = sprite_.layers.id_ = sprite_.id_if hasattr(sprite_, 'life'):s.life = sprite_.lifeif hasattr(sprite_, 'damage'):s.damage = sprite_.damage# Add broadcast sprites to DATA_SET (reset every time a message from client is received).# DATA_SET contains all sprites sent by the client for a specific frame number.# The DATA_SET cannot contains duplicates. The id attribute (memory location)# is used as unique identification number to store sprites in the DATA_SET.# The element in data set represent all active (alive) sprites display on the# client side (before client collision detection).data_set.add(sprite_.id_)# Add the sprite in self.gl.NetGroupAll (if not already in the group) or# update position and texture.# NetGroupAll, will be used in the main loop (locally) to display# all the sprites broadcast from a specific frame number.# If a sprite is not added to that group, it will be ignored# and not display on the client side.if s is not None and len(self.gl.NetGroupAll) > 0:has_ = Falsefor sprites in self.gl.NetGroupAll:if sprites.id_ == s.id_:has_ = Truesprites.rect = s.rectsprites.image = sprite_.imagesprites.frame = sprite_.framebreakif not has_:self.gl.NetGroupAll.add(s)else:self.gl.NetGroupAll.add(s)# Compare NetGroupAll group to DATA_SET and delete sprite(s)# accordingly. Sprites in NetGroupAll and not in DATA_SET will# be killed and remove from every groups they are belonging to.# When a sprite is deleted, the transformation/scale buffer associated# to it will be deleted (using its id).for spr_ in self.gl.NetGroupAll:if spr_.id_ not in data_set:spr_.kill()if spr_.id_ in self.gl.XTRANS_SCALE.keys():self.gl.XTRANS_SCALE.pop(spr_.id_)if spr_.id_ in self.gl.XTRANS_ROTATION.keys():self.gl.XTRANS_ROTATION.pop(spr_.id_)# Reload original texture# for pair in modified_surface.items():# globals()[pair[0]] = pair[1]buffer = b''# data fully received breaking the loop, clear the bufferbreak# pygame.time.wait(1)"""except Exception as error:print('\n[-]SpriteServer - ERROR @ frame: %s : %s %s' % (FRAME, error, time.ctime()))finally:# Clean up the connectionif 'connection' in globals() and connection is not None:connection.close()"""print('\n[-]SpriteServer is now terminated...')def force_quit(host_: str, port_: int) -> None:"""function used for terminating SERVER/ CLIENT threads listening (blocking socket):param host_: string; ip address:param port_: integer; port to use:return: None"""assert isinstance(host_, str), \"Positional argument <host_> is type %s , expecting string." % type(host_)assert isinstance(port_, int), \"Positional argument <port_> is type %s , expecting integer." % type(port_)# todo create assert ( port should be > 1024)sock = Nonetry:sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)sock.connect((host_, port_))data = cpickle.dumps(b"QUIT")sock.sendall(data)print("\n[+]Termination signal sent to SpriteServer...")except Exception as error:print("\n[-]Cannot send termination signal to SpriteServer...")print("\n[-]ERROR %s " % error)finally:if 'sock' in globals() and isinstance(sock, socket.socket):sock.close()def collision_detection():p2_shots = pygame.sprite.Group()p2 = Nonefor sprite_ in GL.NetGroupAll:# detect player spriteif hasattr(sprite_, 'surface'):if sprite_.surface == "P2_SURFACE":p2 = sprite_continue# detect player 2 shots and add them to a specific# group p2_shots. p2_shots has to be destroyed before# leaving the function.elif sprite_.surface == "RED_LASER":p2_shots.add(sprite_)if P1 is not None and P1.alive():# Player 1 collision with asteroid# Use collision mask for collision detection# It is compulsory to have sprite textures with alpha transparency information# in order to check for collision otherwise the collision will be ignored.collision = pygame.sprite.spritecollideany(P1, GL.ASTEROID, collided=pygame.sprite.collide_mask)if collision is not None:P1.collide(collision.damage)if hasattr(collision, 'collide'):collision.collide(P1, P1.damage)else:print(type(collision))raise AttributeError# Player 1 shots collision with asteroids# same than abovefor shots, asteroids in pygame.sprite.groupcollide(GL.PLAYER_SHOTS, GL.ASTEROID, 0, 0, collided=pygame.sprite.collide_mask).items():if asteroids is not None:for aster in asteroids:if hasattr(aster, 'hit'):aster.hit(P1, 100)new_rect = shots.rect.clamp(aster.rect) # todo need to make sure shots is not a listshots.collide(rect_=new_rect, object_=aster)else:print(type(aster))raise AttributeError# Player 2 shots colliding with asteroidif p2 is not None and p2.alive():for shots, asteroids in pygame.sprite.groupcollide(p2_shots, GL.ASTEROID, 0, 0).items(): # ,collided=pygame.sprite.collide_mask).items():if asteroids is not None:for aster in asteroids:if hasattr(aster, 'hit'):aster.hit(None, 100)new_rect = shots.rect.clamp(aster.rect) # todo need to make sure shots is not a listshots.collide(rect_=new_rect, object_=aster)else:print(type(aster))raise AttributeErrorfor spr in GL.NetGroupAll:if spr.id_ == shots.id_:spr.kill()# Use collision mask for collision detection# Check collision between Player 2 and asteroidsif p2 is not None and p2.alive():collision = pygame.sprite.spritecollideany(p2, GL.ASTEROID, collided=pygame.sprite.collide_mask)if collision is not None:# Cannot send damage to player 2, this# is done on the remote host in the collision detection section# Pass only damage to the asteroidcollision.collide(p2.damage)# MirroredTransportClass collision with asteroidif T1 is not None and T1.alive():# todo check collision maskscollision = pygame.sprite.spritecollideany(T1, GL.ASTEROID, collided=pygame.sprite.collide_mask)if collision is not None:T1.collide(collision.damage)if hasattr(collision, 'collide'):collision.collide(T1, T1.damage)else:print(type(collision))raise AttributeErrorp2_shots.remove()p2_shots.empty()del p2_shotsdef window():scrw_half = SCREENRECT.w >> 1scrh_half = SCREENRECT.h >> 1w, h = FRAMEBORDER.get_size()screen.blit(BACKGROUND, (0, 0))screen.blit(FRAMEBORDER, (scrw_half - (w >> 1), scrh_half - (h >> 1)))font = freetype.Font(os.path.join('Assets/Fonts/', 'ARCADE_R.TTF'), size=15)frame_ = FRAME.copy()rect = font.get_rect("Waiting for connection...")font.render_to(frame_, ((frame_.get_width() - rect.w) // 2,(frame_.get_height() - rect.h) // 2),"Waiting for connection...",fgcolor=pygame.Color(255, 255, 255), size=15)screen.blit(frame_, (scrw_half - (w >> 1) + 20, scrh_half - (h >> 1) + 40))clock = pygame.time.Clock()frame = 0while GL.CONNECTION is False:screen.blit(BACKGROUND, (0, 0))pygame.event.pump()Square()GL.All.update()GL.All.draw(screen)screen.blit(frame_, (scrw_half - (w >> 1) + 20, scrh_half - (h >> 1) + 40))GL.TIME_PASSED_SECONDS = clock.tick(70)frame += 1pygame.display.flip()"""frame = 0frame_ = FRAME.copy()for s, t in GL.CLIENTS.items():font.render_to(frame_, ((frame_.get_width() - rect.w) // 2,(frame_.get_height() - rect.h) // 2),'Client connected : ' + str(s) + ':' + str(t),fgcolor=pygame.Color(255, 255, 255), size=15)while 1:pygame.event.pump()screen.blit(BACKGROUND, (0, 0))screen.blit(frame_, (scrw_half - (w >> 1) + 20, scrh_half - (h >> 1) + 40))GL.TIME_PASSED_SECONDS = clock.tick(70)frame += 1pygame.display.flip()..."""class Square(pygame.sprite.Sprite):def __init__(self):self.layer = -1pygame.sprite.Sprite.__init__(self, GL.All)if isinstance(GL.All, pygame.sprite.LayeredUpdates):if self.layer:GL.All.change_layer(self, self.layer)self.image = pygame.Surface((randint(200, 500), randint(200, 500)))self.image.fill((10, 15, 25, 15))self.image.convert(32, pygame.RLEACCEL)self.rect = self.image.get_rect(center=(randint(0, SCREENRECT.w),randint(0, SCREENRECT.h)))self.dt = 0self.blend = pygame.BLEND_RGBA_ADDself.i = 128def update(self):self.image.set_alpha(self.i)self.i -= 10if self.i < 0:self.kill()if __name__ == '__main__':RECT = pygame.sprite.Group()import doctestdoctest.testmod()SERVER = '127.0.0.1'CLIENT = '127.0.0.1'# SERVER = '192.168.0.1'# CLIENT = '192.168.0.4'SCREENRECT = pygame.Rect(0, 0, 800, 1024)GL.SCREENRECT = SCREENRECTscreen = pygame.display.set_mode(SCREENRECT.size, pygame.HWSURFACE, 32)GL.SCREEN = screenpygame.display.set_caption('PLAYER 1')# *********************************************************************# JOYSTICK joystick_count = pygame.joystick.get_count()if joystick_count > 0:joystick = pygame.joystick.Joystick(0)joystick.init()else:joystick = NoneGL.JOYSTICK = joystickfrom Textures import *from Sounds import BLUE_LASER_SOUND, RED_LASER_SOUND, EXPLOSION_SOUND, IMPACT, IMPACT1from Backgrounds import Backgroundfrom Asteroids import Asteroidfrom MessageSender import SpriteClientfrom Transports import Transport# background vectorvector1 = pygame.math.Vector2(x=0, y=0)vector2 = pygame.math.Vector2(x=0, y=-1024)# ********************************************************************# NETWORK SERVER / CLIENT# SpriteServer -> receive client(s) positions# 1) Start the Server to receive client(s) position(s)# If no connection is made, the thread will remains listening/running# in the background, except if an error is raised.server = SpriteServer(GL, SERVER, 1025)server.start()# SpriteClient -> forward all sprites positions# 2) Start the Client to send all sprites positions to client(s)client = SpriteClient(gl_=GL, host_=CLIENT, port_=1024)client.start()# Killing threads if no client connectedif not client.is_alive() or GL.CONNECTION is False:print('No player detected')GL.SPRITE_CLIENT_STOP = TrueGL.SPRITE_SERVER_STOP = Trueforce_quit(SERVER, 1025)window()# *********************************************************************GL.All = LayeredUpdatesModified()GL.ASTEROID = pygame.sprite.Group()GL.PLAYER_SHOTS = pygame.sprite.Group()GL.TRANSPORT = pygame.sprite.GroupSingle()Player1.image = P1_SURFACEPlayer1.containers = GL.AllShot.images = BLUE_LASERShot.containers = GL.All, GL.PLAYER_SHOTSExplosion.containers = GL.AllBackground.containers = GL.AllAfterBurner.containers = GL.AllAsteroid.containers = GL.All, GL.ASTEROIDAsteroid.image = DEIMOSTransport.image = TRANSPORTTransport.containers = GL.All, GL.TRANSPORTBackground.image = BACK1_SBackground(vector_=pygame.math.Vector2(0, 1), position_=vector2, gl_=GL, layer_=-2, event_name_='BACK1_S')Background.image = BACK2_SBackground(vector_=pygame.math.Vector2(0, 1), position_=vector1, gl_=GL, layer_=-2, event_name_='BACK2_S')Background.image = CL1Background(vector_=pygame.math.Vector2(0, 2.5),position_=pygame.math.Vector2(x=0, y=-480),gl_=GL, layer_=-2, blend_=pygame.BLEND_RGB_ADD, event_name_='CL1')Background.image = CL2Background(vector_=pygame.math.Vector2(0, 2.5),position_=pygame.math.Vector2(x=randint(0, 800), y=200),gl_=GL, layer_=-2, blend_=pygame.BLEND_RGB_ADD, event_name_='CL2')ShootingStar.containers = GL.AllShootingStar.image = SHOOTING_STARDisplayScore.containers = GL.AllDisplayScore.images = pygame.Surface((10, 10))GL.P1_SCORE = DisplayScore(gl_=GL, timing_=15)MakeGems.containers = GL.AllP1 = Player1(GL, 15, (screen.get_size()[0] // 2, screen.get_size()[1] // 2))# P1 = NoneT1 = Transport(gl_=GL, timing_=15,pos_=(SCREENRECT.w >> 1,(SCREENRECT.h >> 1) - 100), surface_name_='TRANSPORT', layer_=0)life_bar = HorizontalBar(start_color=pygame.Color(0, 7, 255, 0),end_color=pygame.Color(120, 255, 255, 0),max_=P1.max_life, min_=0,value_=P1.life,start_color_vector=(0, 1, 0), end_color_vector=(0, 0, 0), alpha=False, height=32, xx=200,scan=True)transport_life_bar = HorizontalBar(start_color=pygame.Color(255, 7, 15, 0),end_color=pygame.Color(12, 12, 255, 0),max_=T1.max_life, min_=0,value_=T1.life,start_color_vector=(0, 1, 0), end_color_vector=(0, 0, 0), alpha=False, height=32,xx=200,scan=True)GL.TIME_PASSED_SECONDS = 0clock = pygame.time.Clock()GL.STOP_GAME = FalseFRAME = 0GL.FRAME = 0GL.MIXER = SoundControl(30)f = open('P1_log.txt', 'w')while not GL.STOP_GAME:pygame.event.pump()# print('Server frame # %s vector1 %s vector2 %s' % (FRAME, vector1, vector2))# Send an event to the client triggering the next frameGL.NEXT_FRAME.set() # set the eventevent_obj = EventAttr(event_=GL.NEXT_FRAME, frame_=GL.FRAME)Broadcast(event_obj).next_frame()# Create cosmic dustif GL.FRAME % 10 == 0:if len(COSMIC_DUST_ARRAY) < 15:create_dust(GL)if len(GL.ASTEROID) < 15:asteroid = random.choices(['DEIMOS', 'EPIMET'])[0]scale = random.uniform(0.1, 0.5)rotation = random.randint(0, 360)Asteroid.image = pygame.transform.rotozoom(eval(asteroid).copy(), rotation, scale)GL.ASTEROID.add(Asteroid(asteroid_name_=asteroid,gl_=GL, blend_=0, rotation_=rotation,scale_=scale, timing_=16, layer_=-2))if joystick is not None:JL3 = pygame.math.Vector2(joystick.get_axis(0), joystick.get_axis(1))for event in pygame.event.get():keys = pygame.key.get_pressed()GL.KEYS = keysif event.type == pygame.QUIT:print('Quitting')GL.STOP_GAME = Trueif keys[pygame.K_ESCAPE]:GL.STOP_GAME = Trueif keys[pygame.K_F8]:pygame.image.save(screen, 'dump0.png')if event.type == pygame.MOUSEMOTION:GL.MOUSE_POS = pygame.math.Vector2(event.pos)if random.randint(0, 1000) > 985:shoot = ShootingStar(gl_=GL, layer_=0, timing_=16, surface_name_='SHOOTING_STAR')# update sprites positions and add sprites transformation# At this stage no sprites are display onto the screen. Therefore,# sprite's methods blit directly surface or image onto the screen# will takes the risk to see their surfaces overlay by another sprites surface while calling# any group's draw methods (see below)GL.All.update()# Authorize Player 1 to send data to the client.# Allowing to send only one set of data every frame.# The clear method is used in the class SpriteClient# right after receiving the EventGL.SIGNAL.set()# Always display the group GL.All first has it contains the background surfaces# Any sprite attached to the group GL.All and blit directly to the screen surface# will be override by the network sprite if sprites occupy the same location..# Ideally all sprites should be on the same group in order to draw them ordered by# their layer number.# Todo :# create single group type pygame.sprite.layerUpdatesModified() group by adding network sprites directly# into the GL.All group (also pygame.sprite.layerUpdatesModified())# Note, all network sprites added to the GL.All group have to be killed after being blit onto# the display to avoid filling up the group of new instances (Broadcast class calls will add a# new sprite instances to the group GL.All each frames without killing the previous one, with the# effect of slowing down the game loop and displaying un-necessary sprites.GL.All.draw(screen)# Draw the network sprite above the backgroundif GL.CONNECTION:GL.NetGroupAll.update() # -> run all the update methodGL.NetGroupAll.draw(screen)# *************************************************************# Draw here all the other sprites that does not belongs to# common groups (GL.All & GL.NetGroupAll).# Sprite blit last onto the display are at the top layer.# Be aware that any surface(s) blit with blend attribute will# also blend with the entire sprite scene (blending with# sprites from all layers)# e.g Drawing GUI and life/energy sprite bars, screen bullet impacts# special effects, final score, ending screen and text inputs etc.# Update the sound ControllerGL.MIXER.update()collision_detection()GL.TIME_PASSED_SECONDS = clock.tick(70)GL.SPEED_FACTOR = GL.TIME_PASSED_SECONDS / 1000GL.FPS.append(clock.get_fps())# half = SCREENRECT.w >> 1# safe_zone = pygame.Rect(half - 200, half, 400, SCREENRECT.bottom - half)# pygame.draw.rect(screen, pygame.Color(255, 0, 0, 0), safe_zone, 1)# dust particles (show on the top of all other sprites)if len(COSMIC_DUST_ARRAY) > 0:display_dust(GL)if P1.life > 1:life_bar.VALUE = int(P1.life)life = life_bar.display_gradient()screen.blit(life, (10, 10), special_flags=0)screen.blit(life_bar.display_value(), (50, 10))if T1.life > 1:transport_life_bar.VALUE = int(T1.life)life = transport_life_bar.display_gradient()screen.blit(life, (SCREENRECT.w // 2 + 100, 10), special_flags=0)screen.blit(transport_life_bar.display_value(), (SCREENRECT.w // 2 + 150, 10))pygame.display.flip()FRAME += 1GL.FRAME = FRAMEBroadcast.empty()# for r in GL.All:# print(r)"""print(GL.FRAME)for r in GL.NetGroupAll:f.write('\n NETGROUPALL ' + str(GL.FRAME) + " ")f.write(' Surface: ' + str(r.surface) if hasattr(r, 'surface') else str(r))f.write(' Rect: ' + str(r.rect) if hasattr(r, 'rect') else '')f.write(' id: ' + str(r.id_))for r in GL.All:f.write('\n GL.All ' + str(GL.FRAME) + ' ')f.write(' Surface: ' + str(r.surface) if hasattr(r, 'surface') else str(r))f.write(' Rect: ' + str(r.rect) if hasattr(r, 'rect') else '')f.write(' id: ' + str(r.id_) if hasattr(r, 'id_') else '')"""f.close()GL.SPRITE_CLIENT_STOP = TrueGL.SPRITE_SERVER_STOP = Trueforce_quit(SERVER, 1025)import matplotlib.pyplot as pltplt.title("FPS ")# plt.plot(GL.BYTES_SENT)plt.plot(GL.FPS)plt.draw()plt.show()plt.title("BYTES RECEIVED")plt.plot(GL.BYTES_RECEIVED)plt.draw()plt.show()pygame.quit()運行效果如下圖:
游戲中的玩家1和玩家2
 
GameOver!
 
player1服務(wù)端+玩家1
 player2玩家2(玩家2在大母艦的下面!)
 
看了是不是很激動呢!由于代碼量過多,后續(xù)我回逐個發(fā)出(點擊關(guān)注+點贊不迷路哦!)
總結(jié)
以上是生活随笔為你收集整理的Python3.7版---双人联机雷霆战机(2D特效+音效+道具+Linux系统)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: IETF批准新的互联网标准 防止重放攻击
 - 下一篇: python生成日历_使用python生