zoukankan      html  css  js  c++  java
  • Python3+pygame实现的flappy bird游戏,代码完整,还有音乐

    之前一直在手机上玩flappy bird游戏,闲暇时间就编写了一个

    是采用python3+pygame模块制作而成的,运行效果非常流畅,会让你大吃一惊哦哈哈

    一、运行效果展示

    下载游戏之后,注意在自己的python环境中安装pygame模块,如果没有安装可以使用pip install pygame 进行安装

    然后使用使用命令运行起这个.py文件,运行之后的第一个界面效果如下,是不是很酷炫

    当点击上图中的“Play”按钮之后的效果如下:

    运行之后是有音乐的,大家可以下载代码的时候一起将素材下载,这样就在运行时就能听到音乐

    二、完整代码

    下面代码用到了素材(背景图片,音乐等,下载地址 https://www.itprojects.cn/web/material/details.html?id=16

      1 import math
      2 import os
      3 import time
      4 from  random import randint
      5 from random import uniform
      6 import pygame
      7 from pygame.locals import * #导入一些常用的变量
      8 from collections import deque#加入了队列
      9 
     10 FPS = 60
     11 BK_WIDTH = 900  #背景宽度
     12 BK_HEIGHT = 650  #背景高度
     13 PIPE_WIDTH = 80 #水管的宽度
     14 PIPE_HEIGHT = 10 #水管素材的高度
     15 PIPE_HEAD_HEIGHT = 32#管子头的高度
     16 
     17 #初始化全局变量
     18 BK_MOVE_SPEED = 0.22#主柱子每毫秒移动的速度
     19 ADD_TIME = 2500##每隔多少毫秒就增加一个柱子 这种方法不会有漏洞吗  就是当毫秒数和帧数不匹配啥的  #还需要仔细的思考
     20 TOTAL_PIPE_BODY  =  int(3/5 * BK_HEIGHT)  # 像素值必须为整数   占窗口的3/5
     21 PIPE_RATE =0.96
     22 a_i="bird-wingup"
     23 b_i="bird-wingmid"
     24 c_i="bird-wingdown"
     25 
     26 INITAL_SPEED = -0.37#鸟的Y轴初速度
     27 BIRD_WIDTH = 50
     28 BIRD_HEIGHT = 40
     29 BIRD_INIT_SCORE  = 7#鸟的初始通关分数
     30 
     31 STONE_ADD_TIME = 1000 #每隔多少毫秒就增加一个石头
     32 STONE_WIDTH = 40
     33 STONE_HEIGHT = 30
     34 STONE_LEVEL = 4#石头出现的等级
     35 
     36 BUTTON_WIDTH = 140
     37 BUTT0N_HEIGHT = 60
     38 
     39 BULLET_SPEED = 0.32#子弹的速度
     40 BULLET_WIETH = 50
     41 BULLET_HEIGHT = 30
     42 #设置全局变量 方便修改参数
     43 
     44 
     45 pygame.init()
     46 screen = pygame.display.set_mode((BK_WIDTH,BK_HEIGHT))
     47 pygame.mixer.init()
     48 
     49 music_lose = pygame.mixer.Sound("lose.wav")
     50 music1 = pygame.mixer.Sound("touch.wav")
     51 pygame.mixer.music.load("bkm.mp3")
     52 font = pygame.font.SysFont('comicsansms', 25)
     53 
     54 
     55 #用于设置鸟的种类
     56 def little_bird(list):
     57     global a_i
     58     global b_i
     59     global c_i
     60     a_i=list[0]
     61     b_i=list[1]
     62     c_i=list[2]
     63 
     64 
     65 #用于设置关卡难度
     66 def seteasy(list):
     67     global BK_MOVE_SPEED  # 背景每毫秒移动的速度   就是柱子移动的速度
     68     global ADD_TIME  # 每隔多少毫秒就增加一个柱子
     69     global TOTAL_PIPE_BODY  # 像素值必须为整数   占窗口的3/5
     70     global PIPE_RATE
     71     global STONE_LEVEL  # 鸟出现的等
     72     global BIRD_INIT_SCORE
     73 
     74     BK_MOVE_SPEED = list[0]  # 背景每毫秒移动的速度
     75     ADD_TIME = list[1]  # 每隔多少毫秒就增加一个柱子
     76     TOTAL_PIPE_BODY =list[2]  # 像素值必须为整数   占窗口的3/5
     77     PIPE_RATE = list[3]
     78     Pipe.add_time = list[1]
     79     BIRD_INIT_SCORE = list[4]
     80     STONE_LEVEL = list[5]
     81 
     82 
     83 #子弹类
     84 class Bullet(pygame.sprite.Sprite):
     85     speed = BULLET_SPEED
     86     width = BULLET_WIETH
     87     height = BULLET_HEIGHT
     88 
     89     def __init__(self,bird,images):
     90         super(Bullet,self).__init__() #d调用父类的初始函数 使用此方法 可以减少代码的更改量 并且解决了多重继承的问题
     91         self.x,self.y = bird.x,bird.y
     92         self.bullet = images #给鸟的图片进行赋值
     93         self.mask_bullet = pygame.mask.from_surface(self.bullet)
     94     def update(self):#计算鸟在下一点的新坐标并更新
     95         self.x=self.x+self.speed*frames_to_msec(1)
     96     @property
     97     def image(self):
     98         return self.bullet
     99     @property
    100     def mask(self):
    101         return self.mask_bullet
    102     @property
    103     def rect(self):
    104         return Rect(self.x,self.y,Bullet.width,Bullet.height)
    105     def visible(self):
    106         return 0<self.x<BK_WIDTH+Bullet.width
    107 
    108 
    109 #小鸟做竖直上抛运动  当小鸟加速到一定状态时 就不再加速了
    110 class Bird(pygame.sprite.Sprite):
    111 
    112     width =BIRD_WIDTH #鸟宽
    113     height = BIRD_HEIGHT #鸟长
    114     sink_gravity = 0.001#鸟的下降重力
    115 
    116     def __init__(self,x,y,level,images):
    117         super(Bird,self).__init__() #d调用父类的初始函数 使用此方法 可以减少代码的更改量 并且解决了多重继承的问题
    118         self.x,self.y = x,y
    119         self.wing_up,self.wing_mid,self.wing_down = images #给鸟的图片进行赋值
    120         self.mask_wing_up = pygame.mask.from_surface(self.wing_up)
    121         self.mask_wing_mid = pygame.mask.from_surface(self.wing_mid)
    122         self.mask_wing_down = pygame.mask.from_surface(self.wing_down)
    123         self.inital_speed = 0 #鸟向上的初速度
    124         self.level = level #鸟的初始等级
    125         self.score = 0 #鸟的初始分数为 0
    126 
    127     def update(self,t):#计算鸟在下一点的新坐标并更新
    128         y_ = self.inital_speed*t+0.5*self.sink_gravity*t*t
    129         if self.inital_speed<=0.3:
    130             self.inital_speed = self.inital_speed +self.sink_gravity*t
    131         self.y+=y_   #在主函数里计算时间
    132 
    133     @property
    134     def image(self):
    135         if pygame.time.get_ticks()%400>=120:
    136             return self.wing_up
    137         elif pygame.time.get_ticks()%400>=280:
    138             return self.wing_mid
    139         else:
    140             return self.wing_down
    141     @property
    142     def mask(self):
    143         if pygame.time.get_ticks()%400>=120:
    144             return self.mask_wing_up
    145         elif pygame.time.get_ticks()%400>=280:
    146             return self.mask_wing_mid
    147         else:
    148             return self.mask_wing_down
    149 
    150     @property
    151     def rect(self):
    152         return Rect(self.x,self.y,Bird.width,Bird.height)
    153 
    154 
    155 
    156 class Pipe(pygame.sprite.Sprite):
    157     width = PIPE_WIDTH
    158     pipe_head_height = PIPE_HEAD_HEIGHT
    159     add_time = ADD_TIME
    160 
    161     def __init__(self,pipe_head_image,pipe_body_image):
    162         super(Pipe, self).__init__()
    163         self.x = float(BK_WIDTH-1)
    164         self.score_count = False
    165         self.image = pygame.Surface((Pipe.width,BK_HEIGHT),SRCALPHA)#创建一个surface 我理解为能画到窗口上的对象
    166         # #意为创建一个有ALPHA 通道的surface 如果需要透明就需要这个选项
    167         self.image.convert()
    168         self.image.fill((0,0,0,0))#前三位是颜色  最后一位是透明度
    169         total_pipe_length = TOTAL_PIPE_BODY
    170 
    171         self.bottom_length = randint(int(0.1*total_pipe_length),int(0.8*total_pipe_length))#用于生成指定范围内的整数
    172         self.top_length = total_pipe_length-self.bottom_length
    173 
    174         for i in range(1,self.bottom_length+1):
    175             pos = (0,BK_HEIGHT - i)
    176             self.image.blit (pipe_body_image,pos)#用重叠的技术画出来管子
    177 
    178         bottom_head_y = BK_HEIGHT - self.bottom_length-self.pipe_head_height  #求出管子头的长度
    179         bottom_head_pos = (0,bottom_head_y)
    180         self.image.blit(pipe_head_image,bottom_head_pos)#画管子
    181 
    182         for i in range(-PIPE_HEIGHT,self.top_length-PIPE_HEIGHT):
    183             pos = (0,i)
    184             self.image.blit(pipe_body_image,pos)
    185         top_head_y = self.top_length
    186         self.image.blit(pipe_head_image,(0,top_head_y))
    187 
    188         self.mask = pygame.mask.from_surface(self.image)
    189     @property
    190     def rect(self):
    191         return Rect(self.x,0,Pipe.width,PIPE_HEIGHT)
    192     @property
    193     def visible(self):
    194         return -Pipe.width<self.x<BK_WIDTH
    195 
    196     def update(self,delta_frames=1):
    197         self.x-=BK_MOVE_SPEED*frames_to_msec(delta_frames)
    198 
    199     def collides(self,bird):
    200         return pygame.sprite.collide_mask(self,bird)
    201 
    202 
    203 def change_add_time():
    204     Pipe.add_time= int( (Pipe.add_time*PIPE_RATE) /100)*100
    205 #改变管子的增加时间
    206 
    207 
    208 #石头具有速度 位置等不同属性
    209 #起始的x属性为固定值 y随机 速度在一定范围内随机
    210 class Stone(pygame.sprite.Sprite):
    211     add_time  = STONE_ADD_TIME
    212     width = STONE_WIDTH
    213     height = STONE_HEIGHT
    214     def __init__(self,image):
    215         super(Stone, self).__init__()
    216         self.x =BK_WIDTH-5
    217         self.y = randint(1,int(0.95*BK_HEIGHT))
    218         self.speed = uniform(0.1 ,0.5)
    219         self.stone_image = image
    220         self.mask_image = pygame.mask.from_surface(self.image)
    221 
    222     @property
    223     def rect(self):
    224         return Rect(self.x,self.y,self.width,self.height)
    225     @property
    226     def image(self):
    227         return self.stone_image
    228 
    229     @property
    230     def mask(self):
    231         return self.mask_image
    232 
    233     def update(self,frame = 1):
    234         self.x -= int(self.speed*frames_to_msec(frame))
    235 
    236     def collides(self, b):
    237         return pygame.sprite.collide_mask(self, b)
    238 
    239     def visible(self):
    240         return -self.width<self.x<BK_WIDTH
    241 
    242 
    243 #返回每关需要达到的通关分数
    244 def level_goal(bird):
    245     return bird.level*BIRD_INIT_SCORE
    246 
    247 #载入图片
    248 def load_image(img_file_name):
    249     file_name = os.path.join(".","images",img_file_name)#进行路径字符串的合并
    250     img = pygame.image.load(file_name)
    251     img.convert()
    252     return img
    253 
    254 #根据所在的等级返回需要的背景名
    255 def search_bk(bird):
    256     return "bk"+str(bird.level)
    257 
    258 img_x = load_image('backgroundx.png')#加载背景图像
    259 def load_images():
    260     #加载所有游戏需要用到的图像
    261         #上面写了这个函数下面就用了起来  join用于分隔符和元组的拼接  os.path.join 用于路径的顺序拼接
    262     return {'bk1': load_image('background.png'),
    263              'bk2':load_image("background2.png"),
    264             "bk3":load_image("background3.png"),
    265             "bk4":load_image("background4.png"),
    266             "bk5":load_image("background5.png"),
    267             "bk6":load_image("background6.png"),
    268             'stone':load_image('stone.png'),
    269             'bullet': load_image('bullet.png'),
    270             'pipe-end': load_image('pipe_end.png'),
    271             'pipe-body': load_image('pipe_body.png'),
    272             'f_u': load_image('fenghuang_up.png'),
    273             'f_m': load_image('fenghuang_mid.png'),
    274             'f_w': load_image('fenghuang_down.png'),
    275             'bird-wingup': load_image('bird_wing_up.png'),
    276             'bird-wingmid': load_image('bird_wing_mid.png'),
    277             'bird-wingdown': load_image('bird_wing_down.png')}
    278 
    279 def frames_to_msec(frames,fps=FPS):
    280     return 1000.0*frames/fps   #难道限制的意思就是我可以限制图片出来的时间
    281 
    282 def msec_to_frames(milliseconds, fps=FPS):
    283     return fps * milliseconds / 1000.0#转化成对应的帧数
    284     #转化成每秒的相应的帧数
    285 
    286 
    287 def game_loop():
    288     pygame.mixer.music.play(-1)
    289     pygame.display.set_caption("Flappy Bird")
    290     clock = pygame.time.Clock()#创建一个时钟对象
    291     images = load_images()#建立所有需要的图像字典
    292 
    293     bird = Bird(20,BK_HEIGHT//2,1,(images[a_i],images[b_i] ,images[c_i]))
    294     score_font = pygame.font.SysFont(None,50,bold=True)#名字 大小  粗体  建立画笔  用于记录 分数
    295     score_font2 = pygame.font.SysFont(None, 40, bold=True)  # 名字 大小  粗体  建立画笔  用于记录 分数
    296     score_font3 = pygame.font.SysFont(None, 70, bold=True)  # 名字 大小  粗体  建立画笔  用于记录 分数
    297     pipes = deque()
    298 
    299     stones =pygame.sprite.Group()#将石头新建为一个精灵组
    300     bullets =pygame.sprite.Group()#将子弹新建为一个精灵组
    301 
    302     pause = done = False
    303     frames=0
    304 
    305     while not done :#当没有按下中止键
    306         clock.tick(FPS)
    307         if not (pause or frames%msec_to_frames(Pipe.add_time)):#如果没有按下暂停 或者满足新生成柱子的条件
    308             pp=Pipe(images['pipe-end'], images['pipe-body'])
    309             pipes.append(pp)#生成新管子 并加入队列
    310 
    311         if not (pause or frames%msec_to_frames(Stone.add_time)or bird.level<STONE_LEVEL):
    312             ss = Stone(images["stone"])
    313             stones.add(ss) #加入新生成的石头
    314 
    315         #判断发生了什么事件进行相应的处理
    316         for e in pygame.event.get():
    317             if e.type == QUIT:
    318                 done = True
    319                 break
    320             elif e.type == KEYUP :
    321                 if  e.key == K_p:
    322                     pause = not pause
    323                 elif e.key ==K_d:#发射子弹
    324                     bb=Bullet(bird,images["bullet"])
    325                     bullets.add(bb)
    326                 elif e.key ==K_s or e.key == K_SPACE:
    327                     bird.inital_speed = INITAL_SPEED
    328 
    329 
    330             elif e.type == MOUSEBUTTONUP:
    331                 bird.inital_speed =INITAL_SPEED
    332 
    333             # 重新更新时间
    334             # 使小鸟又进入相应的运动的开始
    335         if pause:
    336             continue  # 这个时段什么都不做
    337 
    338         pygame.sprite.groupcollide(stones,bullets,True,True,pygame.sprite.collide_mask)
    339         pipe_collision = any(p.collides(bird) for p in pipes)
    340         stone_collision = any(s.collides(bird) for s in stones)
    341 
    342         if pipe_collision:
    343             pygame.mixer.music.stop()
    344             done = True
    345             pygame.mixer.Sound.play(music_lose, -1)
    346             time.sleep(3.5)
    347             pygame.mixer.Sound.stop(music_lose)
    348             time.sleep(0.1)
    349 
    350         if stone_collision:
    351             pygame.mixer.music.stop()
    352             pygame.mixer.Sound.play(music_lose, -1)
    353             time.sleep(3.5)
    354             pygame.mixer.Sound.stop(music_lose)
    355             time.sleep(0.1)
    356             done = True
    357         if 0>=bird.y or bird.y>BK_HEIGHT-Bird.height:
    358             done = True
    359             pygame.mixer.music.stop()
    360             pygame.mixer.Sound.play(music_lose, -1)
    361             time.sleep(3.5)
    362             pygame.mixer.Sound.stop(music_lose)
    363             time.sleep(0.1)
    364 
    365 
    366         screen.blit(images[search_bk(bird)], (0, 0))#画背景墙 这种是分开两张的
    367 
    368         while pipes and not pipes[0].visible:
    369             pipes.popleft()#当队列不为空  且管子 0 已经不可见的时候
    370         for s in stones:#删除看不见的石头
    371             if  not s.visible():
    372                 del s
    373         for b in bullets:#删除看不见的子弹
    374             if not b.visible():
    375                 del b
    376 
    377 
    378         for p in pipes:
    379             p.update()
    380             screen.blit(p.image,p.rect)#在指定的位置 画柱子
    381         for s in stones:
    382             s.update()
    383             screen.blit(s.image,s.rect)
    384 
    385         for b in bullets:
    386             b.update()
    387             screen.blit(b.bullet,b.rect)
    388 
    389         for p in pipes:
    390             if bird.x>p.x+Pipe.width and not p.score_count:  #当柱子超过了鸟的位置并且柱子还没有被计分
    391                 bird.score+=1
    392                 p.score_count = True
    393 
    394         sl = score_font.render("level:",True,(255,255,255))
    395         sc = score_font.render("score:",True,(255,255,255))
    396         sl2 = score_font2.render(str(bird.level),True,(255,255,255))
    397         sc2 = score_font2.render(str(bird.score),True,(255,255,255))
    398         screen.blit (sc,(BK_WIDTH-170,20))
    399         screen.blit(sl, (BK_WIDTH - 320, 20))
    400         screen.blit(sc2, (BK_WIDTH - 50, 27))
    401         screen.blit(sl2, (BK_WIDTH - 210, 27))
    402 
    403         bird.update(frames_to_msec(1))#计算一帧所需要的时间
    404         screen.blit(bird.image,bird.rect)
    405 
    406         pygame.display.flip()#绘制图像到屏幕
    407         if bird.score >= level_goal(bird):#如果已经达到了通关分数
    408         #升入下一级  首先要初始化所有变量#清空柱子#改变等级
    409             change_add_time()
    410             pipes.clear()
    411             stones.empty()
    412             bullets.empty()
    413             bird.level += 1  # 分数先暂不做清空后续再加入吧
    414             if bird.level<=6:
    415                 s3 = score_font3.render("Next   Level", True, (255, 255, 255))
    416                 screen.blit(s3, (BK_WIDTH//2-150, BK_HEIGHT//2-50))
    417                 pygame.display.flip()
    418                 time.sleep(2)
    419             if bird.level >6:
    420                 s3 = score_font3.render("You   Win!", True, (255, 255, 255))
    421                 screen.blit(s3, (BK_WIDTH // 2 - 150, BK_HEIGHT // 2 - 50))
    422                 pygame.display.flip()
    423                 time.sleep(2)
    424                 exit()
    425         frames+= 1
    426     pygame.mixer.music.stop()
    427 
    428     Pipe.add_time = ADD_TIME#再次初始化柱子的速度
    429     main()
    430 
    431 
    432 def quit_but():
    433     pygame.quit()
    434     exit()
    435 
    436 
    437 def buttons(x, y, w, h, color, color2, text,action,list=[]):
    438     mouse_position = pygame.mouse.get_pos()
    439     click = pygame.mouse.get_pressed()
    440     if x+w > mouse_position[0] > x and y+h > mouse_position[1] > y:
    441         color = color2
    442         #get_pressed  只返回鼠标三个键是否被按过的状态 不会分辨它是在哪里被按的
    443         if click[0]== 1 and action != None:
    444             pygame.mixer.Sound.play(music1, -1)
    445             time.sleep(0.215)
    446             pygame.mixer.Sound.stop(music1)
    447             if list:
    448                 action(list)
    449             else:
    450                 action()
    451 
    452     pygame.draw.rect(screen, color, (x, y, w, h))
    453     # font = pygame.font.SysFont('comicsansms', 25)
    454     TextSurf = font.render(text, True, (0,0,0))
    455     TextRect = TextSurf.get_rect()
    456     TextRect.center = ((x + (w / 2)), (y + (h / 2)))
    457     screen.blit(TextSurf, TextRect)
    458     pygame.display.update()
    459 
    460 
    461 def setting():
    462     # img = load_image('backgroundx.png')
    463     screen.blit(img_x, (0, 0))  # 画背景墙 这种是分开两张的
    464     pygame.display.flip()
    465     while True:
    466         for event in pygame.event.get():
    467             if event.type==pygame.QUIT:
    468                 exit()
    469 
    470         buttons(100, 200, BUTTON_WIDTH, BUTT0N_HEIGHT,(255, 0, 0), (170, 0, 0), 'easy',seteasy,[0.19,2500,int(5 / 11 * BK_HEIGHT),0.97,5,6])  # 绘制图标  进行事件
    471         buttons(400, 200, BUTTON_WIDTH, BUTT0N_HEIGHT,(0, 255, 0), (0, 170, 0), 'normal', seteasy,[0.19,2500,int(3 / 5 * BK_HEIGHT),0.96,7,4])  # 绘制图标  进行事件
    472         buttons(700 ,200, BUTTON_WIDTH, BUTT0N_HEIGHT,(0, 0, 255), (0, 0, 160),'hard',seteasy,[0.21,1300,int(9 / 14 * BK_HEIGHT),0.96,2,1])  # 绘制图标  进行事件
    473         buttons(700, 550, BUTTON_WIDTH, BUTT0N_HEIGHT, (0, 0, 255), (0, 0, 160), 'back', main)  # 绘制图标  进行事件
    474         buttons(100, 400, BUTTON_WIDTH, BUTT0N_HEIGHT, (255, 0, 0), (170, 0, 0), 'huo lie niao',little_bird,["f_u","f_m","f_w"])  # 绘制图标  进行事件
    475         buttons(400, 400, BUTTON_WIDTH, BUTT0N_HEIGHT, (0, 255, 0), (0, 170, 0),  'xiao niao',little_bird,["bird-wingup","bird-wingmid","bird-wingdown"])  # 绘制图标  进行事件
    476         # buttons(700, 400, BUTTON_WIDTH, BUTT0N_HEIGHT, (0, 0, 255), (0, 0, 160), 'back', main)  # 绘制图标  进行事件
    477 
    478 
    479 def main():
    480     screen.blit(img_x, (0, 0))  # 画背景墙 这种是分开两张的
    481     pygame.display.flip()
    482     while True:
    483         for event in pygame.event.get():
    484             if event.type==pygame.QUIT:
    485                 exit()
    486         buttons((BK_WIDTH-BUTTON_WIDTH)//2,(BK_HEIGHT-BUTT0N_HEIGHT-100)//2,BUTTON_WIDTH,BUTT0N_HEIGHT,(0,255,0),(0,170,0),'Play!',game_loop)#绘制图标  进行事件
    487         buttons((BK_WIDTH - BUTTON_WIDTH) // 2, (BK_HEIGHT - BUTT0N_HEIGHT + 100) // 2, BUTTON_WIDTH, BUTT0N_HEIGHT,(0, 0, 255), (0, 0, 160), 'setting', setting)  # 绘制图标  进行事件
    488         buttons((BK_WIDTH - BUTTON_WIDTH) // 2, (BK_HEIGHT - BUTT0N_HEIGHT + 300) // 2, BUTTON_WIDTH, BUTT0N_HEIGHT,(255, 0, 0), (170, 0, 0), 'Quit', quit_but)
    489 
    490 if __name__ =="__main__":
    491    main()

    上述代码是第1版本,简单起见 没有完全封装为面向对象,等后面有时间再进行完善 目标是:全部用类进行分装,然后拆分到多个模块中

  • 相关阅读:
    【转载】PyQt QSetting保存设置
    Python WebDriver自动化测试
    Pyqt 控件的信号槽事件定义方法
    Pyqt SpVoice朗读功能
    Pyqt 国际化多语言支持
    MQTT研究之EMQ:【wireshark抓包分析】
    MQTT研究之EMQ:【SSL双向验证】
    ES6模板字符串【${}配合反单引号一起用】
    express中遇到的一个小问题“403”
    MQTT研究之EMQ:【基础研究】
  • 原文地址:https://www.cnblogs.com/dong4716138/p/14518217.html
Copyright © 2011-2022 走看看