Python街机模块制作的巫婆和仙女漫游太阳系小故事交互动画by lixingqiu。

这是一个交互动画,本人用python的海龟模块,Pygame模块做过类似的动画,现在又用arcade模块做一下。不过编了个小故事。

"""
巫婆和仙女漫游太阳系小故事动画。

仙女教她的外婆仙术,可是她的外婆由于年纪大了,只学了一半,总也飞不起来。所以仙女送了外婆一把天方夜谭里的神扫帚。
就这样,她们一起离开了地球,在太阳系漫游的不亦乐乎。可是,她们也要呼吸啊,带的氧气不多了。还好,西天取经回来的斗战圣佛孙悟空在暗中已经明察秋毫。
他是不是地变一瓶氧气给她们使用。聪明的仙女一子就发现了太阳系会冒出氧气瓶,她们一起收集了一些氧气,造访了水星、金星、火星、木星、土星、天王星之后,还是玩厌了。
空荡荡的太阳系好无聊,其它星球都不适合人也不适合地神仙玩,所以她们想回地球。可是那时候地球已经被邪恶的宇宙超级大BOSS灭霸给锁住了,她们接近地球时只会被撞死。
还好,有惊奇队长和神奇女侠来帮忙了。惊奇队长安装了一个按扭,只要碰到按扭,那么太阳系行星和太阳的时空就会变慢。
神奇女侠送了神秘香蕉,只要吃了香蕉就能进入地球了。快来玩下这个游戏吧,操作方法:
按上下左右方向箭头或awsd箭头一个角色碰触按钮,让另一个角色顺利的吃到香蕉,然后一起飞回地球。
 
"""
import time
import math
import arcade
import random

SCREEN_WIDTH = 1350
SCREEN_HEIGHT = 780
SCREEN_TITLE = "Python街机动画:巫婆和仙女漫游太阳系动画,作者:李兴球"
MOVEMENT_SPEED = 5

class Planet(arcade.Sprite):
   
    def __init__(self,image,position,period):
        """image:造型图片,position:初始坐标,初始角度"""
        super().__init__(image)
        x,y = position
        self.center_x = SCREEN_WIDTH / 2
        self.center_y = SCREEN_HEIGHT / 2           
        self.a = y * 1.5                        # 长半轴
        self.b = y                              # 短半轴
        self.period = period        
        self.direction = 90
        
    def update(self):
        self.direction = self.direction +   MyGame.timespace * 365 / self.period 
        self.direction = self.direction % 360        
        x = SCREEN_WIDTH / 2 + self.a * math.cos(math.radians(self.direction)) # 根据椭圆参数方程算起始坐标
        y = SCREEN_HEIGHT / 2 + self.b * math.sin(math.radians(self.direction))
        self.center_x = x
        self.center_y = y
        super().update()

class Oxygen(arcade.Sprite):
    """氧气瓶类"""
    def __init__(self,image):
        super().__init__(image,0.5)
        self.center_x = random.randrange(SCREEN_WIDTH)
        self.center_y = random.randrange(SCREEN_HEIGHT)
        self.alpha = 80
        
    
class MyGame(arcade.Window):
    """
    继承自窗口的MyGame类.
    """
    timespace = 1                                             # 游戏的时空变量
    can_home = False                                          # 能回家的逻辑变量
    def __init__(self, width, height, title):
        super().__init__(width, height, title)
        self.frame_counter = 0                                # 帧计数器
        self.planet_list = arcade.SpriteList()                # 新建角色列表,以便统一渲染
        self.all_stars_list = arcade.SpriteList()             # 所有星星列表,包括恒星和行星
        self.oxygen_list = arcade.SpriteList()                # 氧气瓶列表
        self.astronaut_list = arcade.SpriteList()             # 宇航员列表
        
    def setup(self):
        """ 这里是对游戏中的各个对象进行设置 """

        # 背景角色生成
        self.background = arcade.Sprite("bg.png")
        self.background.center_x = SCREEN_WIDTH / 2
        self.background.center_y = SCREEN_HEIGHT / 2

        # 太阳角色生成
        self.sun = arcade.AnimatedTimeSprite()                # 新建动画时序角色
        self.sun.center_x = SCREEN_WIDTH//2
        self.sun.center_y = SCREEN_HEIGHT//2       
        for i in range(0,27):
            self.sun.textures.append(arcade.load_texture("sun/" + str(i) + ".gif"))
        self.all_stars_list.append(self.sun)

        # 行星角色生成
        self.days = [88,225,365,687,4333,10760,30799,60192]
        self.angle_list = [0,32,76,102,47,180,220,330]       # 每个行星的初始旋转角度       

        self.planets_image = ['m.gif','v.gif','e.gif','mars.gif','j.gif','s.gif','u.gif','n.gif']  # 每个行星的图像
        self.xy_list = [(0,y) for y in range(80,510,60)]      # 每个行星的初始坐标                                                      
         
        for i in range(8):                                    # 生成8个行星
            angle = self.angle_list[i]
            position = self.xy_list[i]
            image = self.planets_image[i]
            period = self.days[i]
            if i == 2 :          # 如果是地球的参数
                self.earth = Planet(image,position,period)  # 地球不添加到所有行星列表
                self.all_stars_list.append(self.earth)
            else:
                aplanet = Planet(image,position,period)         # 新建行星
                self.planet_list.append(aplanet)                 # 添加到所有行星列表   
                self.all_stars_list.append(aplanet)              # 添加到所有星星列表
            

            
        # 宇航员角色生成
        self.astronaut1 = arcade.Sprite("princess.png",0.25)  # 宇航员1角色,第二个参数是比例
        self.astronaut1.status = "live"                       # 自定义属生,
        self.astronaut1.oxygens = 1000                        # 自定义属性,氧气数量
        self.astronaut1.center_x = 100
        self.astronaut1.center_y = 100
        self.astronaut2 = arcade.Sprite("witch.png",0.25)     # 宇航员2角色,第二个参数是比例
        self.astronaut2.status = "live"                       # 自定义属生,
        self.astronaut2.oxygens = 1000                        # 自定义属性,氧气数量
        self.astronaut2.center_x = SCREEN_WIDTH-100
        self.astronaut2.center_y = SCREEN_HEIGHT-100
        self.astronaut_list.append(self.astronaut1)
        self.astronaut_list.append(self.astronaut2)
        

        # 按钮角色生成
        self.switchbutton = arcade.Sprite("button2-a.png")    # 新建一个按钮,蓝色的
        self.switchbutton.textures.append(arcade.load_texture("button2-b.png")) # 橙色的造型
        self.switchbutton.center_x = 100
        self.switchbutton.center_y = 500
        
        # 回地球的道具
        self.home_glob = arcade.Sprite("bananas.png")        # 回地球的道具
        self.home_glob.center_x = SCREEN_WIDTH//2 + 100
        self.home_glob.center_y = SCREEN_HEIGHT//2 - 100
        

        # 在随机位置产生几个氧气瓶  ,可以单独定义函数让它不在星星的位置生成   
        [self.oxygen_list.append(Oxygen("cloud.png")) for i in range(3)]
            
 
    def on_key_press(self,key,modifiers):        

        if self.astronaut1.status == "live":
            if key == arcade.key.UP:
                self.astronaut1.change_y = MOVEMENT_SPEED
                
                self.astronaut1.angle = 90
            elif key == arcade.key.DOWN:
                self.astronaut1.change_y = -MOVEMENT_SPEED
                self.astronaut1.angle = -90
            elif key == arcade.key.LEFT:
                self.astronaut1.change_x = -MOVEMENT_SPEED
                
                self.astronaut1.angle =  180
            elif key == arcade.key.RIGHT:
                self.astronaut1.change_x = MOVEMENT_SPEED
                
                self.astronaut1.angle =  0
                

        if self.astronaut2.status == "live":
            if key == arcade.key.W:
                self.astronaut2.change_y = MOVEMENT_SPEED
                self.astronaut2.angle = 90
            elif key == arcade.key.S:
                self.astronaut2.change_y = -MOVEMENT_SPEED
                self.astronaut2.angle = -90
            elif key == arcade.key.A:
                self.astronaut2.change_x = -MOVEMENT_SPEED
                self.astronaut2.angle =  180
            elif key == arcade.key.D:
                self.astronaut2.change_x = MOVEMENT_SPEED
                self.astronaut2.angle =  0
            
            
    def on_key_release(self, key, modifiers):
        """松开按键. """
        if self.astronaut1.status == "live":
            if key == arcade.key.UP or key == arcade.key.DOWN:
                self.astronaut1.change_y = 0
            elif key == arcade.key.LEFT or key == arcade.key.RIGHT:
                self.astronaut1.change_x = 0
                
        if self.astronaut2.status == "live":
            if key == arcade.key.W  or key == arcade.key.S:
                self.astronaut2.change_y = 0
            elif key == arcade.key.A or key == arcade.key.D:
                self.astronaut2.change_x = 0
                
        
    def update(self, x):
        """每帧更新游戏内在逻辑"""
        
        self.frame_counter += 1                                 # 帧计数
        if self.frame_counter % 180 == 0 and len(self.oxygen_list)<20: # 2秒左右产生一个氧气瓶
            self.oxygen_list.append(Oxygen("cloud.png"))

        if self.astronaut1.status == "live":
           if self.frame_counter % 10 == 0:                     # 宇航员的氧气数减1
               if self.astronaut1.oxygens > 0:                  # 氧气数大于0,则不断减少,否则坠
                  self.astronaut1.oxygens -= 1
                 
               if self.astronaut1.oxygens == 0:                 # 宇航员1氧气为0,死了
                   self.astronaut1.status  = "dead"             # 状态为死
                   self.astronaut1.change_y = -10
                   
                
        if self.astronaut2.status == "live":
           if self.frame_counter % 10 == 0:                     # 宇航员2的氧气数减1
               if self.astronaut2.oxygens > 0:                  # 氧气数大于0,则不断减少,否则坠
                  self.astronaut2.oxygens -= 1
                 
               if self.astronaut2.oxygens == 0:                 # 宇航员2氧气为0,死了
                   self.astronaut2.status  = "dead"             # 状态为死
                   self.astronaut2.change_y = -10                   
                
                
        # 宇航员1的碰撞检测
        if  self.astronaut1.status  == "live":            
            oxygen_list1  = arcade.check_for_collision_with_list(self.astronaut1,self.oxygen_list) # 宇航员1和氧气瓶的碰撞检测
            for oxygen in oxygen_list1:
                oxygen.kill()
                self.astronaut1.oxygens += 100          
                    
            p = arcade.check_for_collision_with_list(self.astronaut1,self.all_stars_list)            # 宇航员1和星星的碰撞检测            
            if len(p) > 0:             
               self.astronaut1.oxygens = 0
      
              
        # 宇航员2的碰撞检测
        if  self.astronaut2.status  == "live": 
            oxygen_list2  = arcade.check_for_collision_with_list(self.astronaut2,self.oxygen_list) # 宇航员2和氧气瓶的碰撞检测
            for oxygen in oxygen_list2:
                oxygen.kill()
                self.astronaut2.oxygens += 100           
               
            p = arcade.check_for_collision_with_list(self.astronaut2,self.all_stars_list)            # 宇航员2和星星的碰撞检测            
            if len(p) > 0:             
               self.astronaut2.oxygens = 0
    
        b = arcade.check_for_collision_with_list(self.switchbutton,self.astronaut_list)        # 按钮和宇航员们碰撞检测
        if b :
           self.switchbutton.set_texture(1)                                                    # 切换到橙色按钮
           MyGame.timespace = 0.1
        else:
           self.switchbutton.set_texture(0)
           MyGame.timespace = 1
           
        if not MyGame.can_home:
            b = arcade.check_for_collision_with_list(self.home_glob,self.astronaut_list)     # 回家道具和宇航员们的碰撞检测
            if b : MyGame.can_home = True
        else:
            a_list = arcade.check_for_collision_with_list(self.earth,self.astronaut_list)     # 回家道具和宇航员们的碰撞检测
            for a in a_list:
                a.kill()                  
      
        self.all_stars_list.update()
        self.home_glob.update()
        self.oxygen_list.update()
        self.astronaut_list.update()
        if self.astronaut1.status=="live":
            if self.astronaut1.left <=0  :self.astronaut1.left=0
            if self.astronaut1.right >= SCREEN_WIDTH-1:self.astronaut1.right=SCREEN_WIDTH-1        
            if self.astronaut1.bottom <=0  :self.astronaut1.bottom=0
            if self.astronaut1.top >= SCREEN_HEIGHT-1:self.astronaut1.top=SCREEN_HEIGHT-1
        if self.astronaut2.status=="live":
            if self.astronaut2.left <=0  :self.astronaut2.left=0
            if self.astronaut2.right >= SCREEN_WIDTH-1:self.astronaut2.right=SCREEN_WIDTH-1        
            if self.astronaut2.bottom <=0  :self.astronaut2.bottom=0
            if self.astronaut2.top >= SCREEN_HEIGHT-1:self.astronaut2.top=SCREEN_HEIGHT-1        

        i = int(1/MyGame.timespace)     # 时空变慢,太阳换造型的时间变慢
        if self.frame_counter % i == 0:
            self.sun.update_animation() # 太阳的坐标不变,所以没有调用它的update
 
    
    def on_draw(self):
        """
        渲染屏幕        """

        arcade.start_render()        # 用背景色填充屏幕        
        self.background.draw()       #  画背景 
        self.switchbutton.draw()     
        if not MyGame.can_home:self.home_glob.draw()
        self.sun.draw()
        self.all_stars_list.draw()   # 所有星星一起渲染
        self.oxygen_list.draw()
        self.astronaut_list.draw()
        
        if  self.astronaut1.status  == "live": 
            output = f"宇航员1氧气数: {self.astronaut1.oxygens}"
            arcade.draw_text(output, 10, 20, arcade.color.WHITE, 14 ,font_name='simkai')
        if  self.astronaut2.status  == "live": 
            output = f"宇航员2氧气数: {self.astronaut2.oxygens}"
            arcade.draw_text(output, 200, 20, arcade.color.WHITE, 14 ,font_name='simkai')

        if MyGame.can_home and len(self.astronaut_list) == 0 :
            output = f"恭喜,宇航员们安全返回了地球"
            arcade.draw_text(output, SCREEN_WIDTH//2-300, SCREEN_HEIGHT//2, arcade.color.CYAN, 34 ,font_name='simhei',align='center')
            
 

def main():
    """ Main method """
    window = MyGame(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
    window.setup()
    arcade.run()


if __name__ == "__main__":
    main()

请单击图片查看动画