An Introduction to Interactive Programming in Python 最后的作业 -- 太空战机

游戏本身可玩性不高,为实现而实现,对象的封装和逻辑控制有点混乱。

先上图:




以下是原代码:

import simplegui
import math
import random

# globals for user interface
WIDTH = 800
HEIGHT = 600
score = 0
lives = 3
time = 0.5
started = False

class ImageInfo:
    def __init__(self, center, size, radius = 0, lifespan = None, animated = False):
        self.center = center
        self.size = size
        self.radius = radius
        if lifespan:
            self.lifespan = lifespan
        else:
            self.lifespan = float('inf')
        self.animated = animated

    def get_center(self):
        return self.center

    def get_size(self):
        return self.size

    def get_radius(self):
        return self.radius

    def get_lifespan(self):
        return self.lifespan

    def get_animated(self):
        return self.animated

# art assets created by Kim Lathrop, may be freely re-used in non-commercial projects, please credit Kim

# debris images - debris1_brown.png, debris2_brown.png, debris3_brown.png, debris4_brown.png
#                 debris1_blue.png, debris2_blue.png, debris3_blue.png, debris4_blue.png, debris_blend.png
debris_info = ImageInfo([320, 240], [640, 480])
debris_image = simplegui.load_image("http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/debris2_blue.png")

# nebula images - nebula_brown.png, nebula_blue.png
nebula_info = ImageInfo([400, 300], [800, 600])
nebula_image = simplegui.load_image("http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/nebula_blue.f2014.png")

# splash image
splash_info = ImageInfo([200, 150], [400, 300])
splash_image = simplegui.load_image("http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/splash.png")

# ship image
ship_info = ImageInfo([45, 45], [90, 90], 35)
ship_image = simplegui.load_image("http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/double_ship.png")

# missile image - shot1.png, shot2.png, shot3.png
missile_info = ImageInfo([5,5], [10, 10], 3, 50)
missile_image = simplegui.load_image("http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/shot2.png")

# asteroid images - asteroid_blue.png, asteroid_brown.png, asteroid_blend.png
asteroid_info = ImageInfo([45, 45], [90, 90], 40)
asteroid_image = simplegui.load_image("http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/asteroid_blue.png")

# animated explosion - explosion_orange.png, explosion_blue.png, explosion_blue2.png, explosion_alpha.png
explosion_info = ImageInfo([64, 64], [128, 128], 17, 24, True)
explosion_image = simplegui.load_image("http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/explosion_alpha.png")

# sound assets purchased from sounddogs.com, please do not redistribute
# .ogg versions of sounds are also available, just replace .mp3 by .ogg
soundtrack = simplegui.load_sound("http://commondatastorage.googleapis.com/codeskulptor-assets/sounddogs/soundtrack.mp3")
missile_sound = simplegui.load_sound("http://commondatastorage.googleapis.com/codeskulptor-assets/sounddogs/missile.mp3")
missile_sound.set_volume(.5)
ship_thrust_sound = simplegui.load_sound("http://commondatastorage.googleapis.com/codeskulptor-assets/sounddogs/thrust.mp3")
explosion_sound = simplegui.load_sound("http://commondatastorage.googleapis.com/codeskulptor-assets/sounddogs/explosion.mp3")

# helper functions to handle transformations
def angle_to_vector(ang):
    return [math.cos(ang), math.sin(ang)]

def dist(p,q):
    return math.sqrt((p[0] - q[0]) ** 2+(p[1] - q[1]) ** 2)

# Ship class
class Ship:
    def __init__(self, pos, vel, angle, image, info):
        self.pos = [pos[0],pos[1]]
        self.vel = [vel[0],vel[1]]
        self.thrust = False
        self.angle = angle
        self.angle_vel = 0
        self.image = image
        self.image_center = info.get_center()
        self.image_size = info.get_size()
        self.radius = info.get_radius()
        self.missile_list = []
        self.missile_inteval = 10
        self.missile_life = 1 * 60

    def get_missiles(self):
        return self.missile_list

    def set_missile(self,missiles):
        self.missile_list = missiles

    def get_pos(self):
        return self.pos

    def get_radius(self):
        return self.radius

    def set_move(self,move):
        if type(move) == float:
            self.angle_vel = move
        elif type(move) == bool:
            self.thrust = move
        else:
            move(self)

    def shoot(self):
        ang_vec = angle_to_vector(self.angle)
        canon_pos = [self.pos[0] + ang_vec[0] * (self.image_size[0] / 2), self.pos[1] + ang_vec[1] * (self.image_size[0] / 2)]
        if len(self.missile_list) == 0 or dist(self.missile_list[0][0].get_pos(),canon_pos) > self.missile_inteval:
            a_missile = Sprite(canon_pos,
                           [self.vel[0] + ang_vec[0] * 5,self.vel[1] + ang_vec[1] * 5],
                           0, 0, missile_image, missile_info, missile_sound)
            self.missile_list.insert(0,[a_missile,self.missile_life])

    def stop_shoot(self):
        pass

    def draw(self,canvas):
        pos = [self.image_center[0] + self.image_size[0], self.image_center[1]] if self.thrust else self.image_center
        canvas.draw_image(self.image, pos, self.image_size, self.pos,self.image_size,self.angle)
        for a_missile in self.missile_list:
            a_missile[0].draw(canvas)

    def update(self):
        self.angle += self.angle_vel
        vel_acc = 1.5 if self.thrust else 0
        ang_vel = angle_to_vector(self.angle)
        for i in range(2):
            self.vel[i] = 0.92 * self.vel[i] + ang_vel[i] * vel_acc
            self.pos[i] += self.vel[i]
        self.pos[0] = self.pos[0] % WIDTH
        self.pos[1] = self.pos[1] % HEIGHT
        m_l = self.missile_list[:]
        for a_missile in m_l:
            if a_missile[1] > 0:
                a_missile[1] -= 1
                a_missile[0].update()
            else:
                self.missile_list.remove(a_missile)

# Sprite class
class Sprite:
    def __init__(self, pos, vel, ang, ang_vel, image, info, sound = None):
        self.pos = [pos[0],pos[1]]
        self.vel = [vel[0],vel[1]]
        self.angle = ang
        self.angle_vel = ang_vel
        self.image = image
        self.image_center = info.get_center()
        self.image_size = info.get_size()
        self.radius = info.get_radius()
        self.lifespan = info.get_lifespan()
        self.animated = info.get_animated()
        self.age = 0
        self.animate_count = 0
        if sound:
            sound.rewind()
            sound.play()

    def animation_over(self):
        return self.animate_count >= self.lifespan * 2

    def set_pos(self,pos):
        self.pos = pos

    def get_pos(self):
        return self.pos

    def get_radius(self):
        return self.radius

    def draw(self, canvas):
        if self.animated and self.lifespan and self.animate_count < self.lifespan * 2:
            canvas.draw_image(self.image, [self.image_center[0] + (self.animate_count / 2) * self.image_size[0],self.image_center[1]],
                                  self.image_size, self.pos, self.image_size,self.angle)
            self.animate_count += 1
        else:
            canvas.draw_image(self.image, self.image_center, self.image_size, self.pos, self.image_size,self.angle)

    def update(self):
        self.angle += self.angle_vel
        for i in range(2):
            self.pos[i] += self.vel[i]
        self.pos[0] = self.pos[0] % WIDTH
        self.pos[1] = self.pos[1] % HEIGHT

def is_collide(first,second):
    return True if dist(first.get_pos(),second.get_pos()) < (first.get_radius() + second.get_radius()) else False

def draw(canvas):
    global time, score, lives, started

    # animiate background
    time += 1
    wtime = (time / 4) % WIDTH
    center = debris_info.get_center()
    size = debris_info.get_size()
    canvas.draw_image(nebula_image, nebula_info.get_center(), nebula_info.get_size(), [WIDTH / 2, HEIGHT / 2], [WIDTH, HEIGHT])
    canvas.draw_image(debris_image, center, size, (wtime - WIDTH / 2, HEIGHT / 2), (WIDTH, HEIGHT))
    canvas.draw_image(debris_image, center, size, (wtime + WIDTH / 2, HEIGHT / 2), (WIDTH, HEIGHT))

    deal_with_collide(canvas)

    canvas.draw_text("Lives: " + str(lives), [40,40], 20, "Red")
    canvas.draw_text("Score: " + str(score), [620,40], 20, "Red")

    for a_explosion in list(explosion_set):
        a_explosion.draw(canvas)
        if a_explosion.animation_over():
            explosion_set.discard(a_explosion)
    # draw ship and sprites
    # update ship and sprites
    if started:
        my_ship.draw(canvas)
        my_ship.update()
    for a_rock in list(rock_set):
        a_rock.draw(canvas)
        a_rock.update()

    # draw splash screen if not started
    if not started:
        canvas.draw_image(splash_image, splash_info.get_center(),
                          splash_info.get_size(), [WIDTH / 2, HEIGHT / 2],
                          splash_info.get_size())
        timer.stop()
        for rock in list(rock_set):
            rock_set.discard(rock)
        for explosion in list(explosion_set):
            explosion_set.discard(explosion)

vel_list = {simplegui.KEY_MAP['left']: (-0.3,0.0),
            simplegui.KEY_MAP['right']: (0.3,0.0),
            simplegui.KEY_MAP['up']: (True,False),
            simplegui.KEY_MAP['space']: (Ship.shoot,Ship.stop_shoot)}

def keydown(key):
    if not started:
        return
    if key in vel_list:
        my_ship.set_move(vel_list[key][0])
    if key == simplegui.KEY_MAP['up']:
        ship_thrust_sound.play()

def keyup(key):
    if not started:
        return
    if key in vel_list:
        my_ship.set_move(vel_list[key][1])
    if key == simplegui.KEY_MAP['up']:
        ship_thrust_sound.pause()
        ship_thrust_sound.rewind()

# mouseclick handlers that reset UI and conditions whether splash image is drawn
def click(pos):
    global started, lives, score, my_ship
    center = [WIDTH / 2, HEIGHT / 2]
    size = splash_info.get_size()
    inwidth = (center[0] - size[0] / 2) < pos[0] < (center[0] + size[0] / 2)
    inheight = (center[1] - size[1] / 2) < pos[1] < (center[1] + size[1] / 2)
    if (not started) and inwidth and inheight:
        started = True
        lives = 3
        score = 0
        my_ship = Ship([WIDTH / 2, HEIGHT / 2], [0, 0], 0, ship_image, ship_info)
        timer.start()

# timer handler that spawns a rock
def rock_spawner():
    global rock_set
    if len(rock_set) < 13:
        rand_pos = [random.randrange(0, WIDTH), random.randrange(0, HEIGHT)]
        rand_vel = [random.random() * .6 - .3, random.random() * .6 - .3]
        a_rock = Sprite(rand_pos, rand_vel, 0, 0.1, asteroid_image, asteroid_info)
        if not is_collide(a_rock,my_ship):
            rock_set.add(a_rock)

def deal_with_collide(canvas):
    global lives, started, score
    mis_list = my_ship.get_missiles()
    for a_rock in list(rock_set):
        for missile in mis_list[:]:
            if is_collide(missile[0],a_rock):
                mis_list.pop(mis_list.index(missile))
                pos = a_rock.get_pos()
                explosion_set.add(Sprite(pos, [0, 0], 0, 0, explosion_image, explosion_info))
                explosion_sound.play()
                rock_set.discard(a_rock)
                score += 1
    my_ship.set_missile(mis_list)
    for a_rock in list(rock_set):
        if is_collide(my_ship,a_rock):
            pos = a_rock.get_pos()
            explosion_set.add(Sprite(pos, [0, 0], 0, 0, explosion_image, explosion_info))
            explosion_sound.play()
            lives -= 1
            rock_set.discard(a_rock)
            if lives == 0:
                started = False

# initialize frame
frame = simplegui.create_frame("Asteroids", WIDTH, HEIGHT)

check_collide = is_collide
# initialize ship and two sprites
my_ship = Ship([WIDTH / 2, HEIGHT / 2], [0, 0], 0, ship_image, ship_info)
rock_set = set()
#a_rock = Sprite([WIDTH / 3, HEIGHT / 3], [1, 1], 0, 0.1, asteroid_image, asteroid_info)
#a_missile = Sprite([2 * WIDTH / 3, 2 * HEIGHT / 3], [-1,1], 0, 0, missile_image, missile_info, missile_sound)
#a_explosion = Sprite([WIDTH / 3, HEIGHT / 3], [0, 0], 0, 0, explosion_image, explosion_info)
explosion_set = set()

# register handlers
frame.set_draw_handler(draw)
frame.set_keydown_handler(keydown)
frame.set_mouseclick_handler(click)
frame.set_keyup_handler(keyup)

timer = simplegui.create_timer(1000.0, rock_spawner)

# get things rolling
timer.start()
frame.start()
# soundtrack.rewind()
soundtrack.play()


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Python交互式编程入门》是一门介绍如何使用Python进行交互式编程的课程。在这门课程中,学习者将学习如何使用Python编写简单的程序并与之进行交互。 首先,课程将介绍Python的基本语法和编程概念。学习者将学习如何定义变量、使用条件语句和循环、编写函数以及处理列表和字典等数据结构。 接下来,课程将介绍Python交互式编程的概念。学习者将学习如何使用Python的交互式命令行界面进行编程,并了解如何与Python解释器进行交互。他们将学习如何编写单行和多行的Python代码,并立即查看结果。 在这门课程中,学习者还将学习如何使用Python编写简单的图形用户界面(GUI)应用程序。他们将学习如何使用Python的GUI库来创建窗口、按钮、文本框等界面元素,并学习如何为这些元素添加交互功能。 此外,课程还将涵盖一些常见的Python编程任务,如文件操作、网络编程和数据可视化。通过这些任务的实践,学习者将能够更好地理解Python交互式编程的应用。 该课程注重实践,学习者将通过编写小型项目和解决编程问题来应用所学知识。他们将有机会与其他学习者一起合作,分享代码和解决方案,并从其他人的经验中学习。 总之,《Python交互式编程入门》是一门适合初学者的课程,它将帮助学习者掌握Python编程的基本概念和技能,并提供实践经验和项目练习。通过这门课程的学习,学习者将能够熟练地运用Python进行交互式编程,并在日后的编程任务中受益。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值