首页 > 代码库 > Mini projects ---- Pong

Mini projects ---- Pong

课程全名:An Introduction to Interactive Programming in Python,来自 Rice University

授课教授:Joe Warren, Scott Rixner, John Greiner, Stephen Wong

工具:http://www.codeskulptor.org/, simplegui 模块

 

第四次作业,完成一个弹球的游戏,游戏的界面如下,规则也很简单,球不断在两边的paddle之间撞击后速度会不断加快,直到有一名玩家无法将球回击,则另一名玩家得分。

image

 

之前得了解一下控制物体移动的方式。

假设物体与点p相对应,第一种方式是直接通过控制点p的坐标来实现移动

在draw_handler中不用进行更新,只用绘制他的position就可以

坐标的变化放在key_handler中进行修改,比如

key_handler:

    Left Arrow –> p[0] –= c

    Right Arrow –> p[0] += c

    Up Arrow –> p[1] –= c

    Down Arrow –> p[1] += c

代码如下:

import simpleguiWIDTH = 500HEIGHT = 300p = [WIDTH/2, HEIGHT/2]def draw(canvas):    canvas.draw_circle(p, 30, 1, "White", "White")def key_down(key):    c = 4    if key == simplegui.KEY_MAP[up]:        p[1] -= c    elif key == simplegui.KEY_MAP[down]:        p[1] +=c    elif key == simplegui.KEY_MAP[left]:        p[0] -= c    elif key == simplegui.KEY_MAP[right]:        p[0] += c    # Create a frame and assign callbacks to event handlersframe = simplegui.create_frame("Position Control", WIDTH, HEIGHT)frame.set_draw_handler(draw)frame.set_keydown_handler(key_down)# Start the frame animationframe.start()

第二种方式通过控制点p的速率(velocity)变化来实现移动。

draw_handler中进行坐标的更新和绘制

在key_handler中进行速率的调整

draw_handler:

    p[0] += v[0]

    p[1] += v[1]

key_handler:

    Left Arrow –> v[0] –= c

    Right Arrow –> v[0] += c

    Up Arrow –> v[1] –= c

    Down Arrow –> v[1] += c

代码如下:

import simpleguiWIDTH = 500HEIGHT = 300v = [0, 0]p = [WIDTH/2, HEIGHT/2]def draw(canvas):    p[0] += v[0]    p[1] += v[1]    canvas.draw_circle(p, 30, 1, "White", "White")def key_down(key):    c = 1    if key == simplegui.KEY_MAP[up]:        v[1] -= c    elif key == simplegui.KEY_MAP[down]:        v[1] +=c    elif key == simplegui.KEY_MAP[left]:        v[0] -= c    elif key == simplegui.KEY_MAP[right]:        v[0] += c    # Create a frame and assign callbacks to event handlersframe = simplegui.create_frame("Position Control", WIDTH, HEIGHT)frame.set_draw_handler(draw)frame.set_keydown_handler(key_down)# Start the frame animationframe.start()

 

这里再提一下碰撞的处理,上下碰撞,只用把v[1]的方向取反,左右碰撞,把v[0]的方向取反。

主要就是边界位置的判断,对于一个假定中心为p,半径为r的求来说:

碰撞如下:

Left Wall:

p[0] <= r

Right Wall:

p[0] >= (width-1)-r

Top Wall:

p[1] <= r

Bottom Wall:

p[1] >= (height-1)-r

下面两张图来自老师的课件,关于定义

image

image

 

回归到这个游戏,基本要用到的知识也就这么多,移动paddle处理方式稍微特殊一点,keydown_handler进行velocity的正方向增加,那么keyup_handler进行velocity的负方向增加,这样就可以实现,按下按键后paddle持续移动,释放按键后paddle停止移动。

对于小球,初始随机给一个velocity,只要不是垂直或者水平,然后处理碰撞,在进行paddle碰撞后,要给予小球10%velocity上的增量。

 

完整代码如下:

# Implementation of classic arcade game Pongimport simpleguiimport random# initialize globals - pos and vel encode vertical info for paddlesWIDTH = 600HEIGHT = 400       BALL_RADIUS = 20PAD_WIDTH = 8PAD_HEIGHT = 80HALF_PAD_WIDTH = PAD_WIDTH / 2HALF_PAD_HEIGHT = PAD_HEIGHT / 2LEFT = FalseRIGHT = True# initialize ball_pos and ball_vel for new bal in middle of table# if direction is RIGHT, the ball‘s velocity is upper right, else upper leftdef spawn_ball(direction):    global ball_pos, ball_vel # these are vectors stored as lists    ball_pos = [WIDTH/2, HEIGHT/2]    ball_dir = 1 if direction == RIGHT else -1    ball_vel = [ball_dir * random.randrange(2, 4), -random.randrange(1, 3)]# define event handlersdef new_game():    global paddle1_pos, paddle2_pos, paddle1_vel, paddle2_vel  # these are numbers    global score1, score2  # these are ints    spawn_ball(random.choice([LEFT, RIGHT]))    paddle1_pos, paddle2_pos = HEIGHT / 2, HEIGHT / 2    paddle1_vel, paddle2_vel = 0, 0    score1, score2 = 0, 0        def draw(canvas):    global score1, score2, paddle1_pos, paddle2_pos, ball_pos, ball_vel     # draw mid line and gutters    canvas.draw_line([WIDTH / 2, 0],[WIDTH / 2, HEIGHT], 1, "White")    canvas.draw_line([PAD_WIDTH, 0],[PAD_WIDTH, HEIGHT], 1, "White")    canvas.draw_line([WIDTH - PAD_WIDTH, 0],[WIDTH - PAD_WIDTH, HEIGHT], 1, "White")            # update ball    new_ball_posX = ball_pos[0] + ball_vel[0]    new_ball_posY = ball_pos[1] + ball_vel[1]    if new_ball_posY <= BALL_RADIUS:        ball_pos[1] = BALL_RADIUS        ball_vel[1] *= -1    elif new_ball_posY >= HEIGHT - 1 - BALL_RADIUS:        ball_pos[1] = HEIGHT - 1 - BALL_RADIUS        ball_vel[1] *= -1    else:        ball_pos[1] = new_ball_posY            # hit the left gutter    if new_ball_posX <= PAD_WIDTH + BALL_RADIUS:        ball_pos[0] = PAD_WIDTH + BALL_RADIUS        if (ball_pos[1] >= paddle1_pos - HALF_PAD_HEIGHT             and ball_pos[1] <= paddle1_pos + HALF_PAD_HEIGHT):            ball_vel[0] = -(ball_vel[0] + 0.1 * ball_vel[0])            ball_vel[1] = ball_vel[1] + 0.1 * ball_vel[1]        else:            score2 = score2 + 1            spawn_ball(RIGHT)                # hit the right gutter    elif new_ball_posX >= WIDTH - PAD_WIDTH - BALL_RADIUS:        ball_pos[0] = WIDTH - PAD_WIDTH - BALL_RADIUS        if (ball_pos[1] >= paddle2_pos - HALF_PAD_HEIGHT             and ball_pos[1] <= paddle2_pos + HALF_PAD_HEIGHT):            ball_vel[0] = -(ball_vel[0] + 0.1 * ball_vel[0])            ball_vel[1] = ball_vel[1] + 0.1 * ball_vel[1]        else:            score1 = score1 + 1            spawn_ball(LEFT)    else:        ball_pos[0] = new_ball_posX            # draw ball    canvas.draw_circle(ball_pos, BALL_RADIUS, 1, "White", "White")        # update paddle‘s vertical position, keep paddle on the screen    new_paddle1_pos = paddle1_pos + paddle1_vel    if new_paddle1_pos <= HALF_PAD_HEIGHT:        paddle1_pos = HALF_PAD_HEIGHT    elif new_paddle1_pos >= HEIGHT-HALF_PAD_HEIGHT:        paddle1_pos = HEIGHT-HALF_PAD_HEIGHT    else:        paddle1_pos = new_paddle1_pos        new_paddle2_pos = paddle2_pos + paddle2_vel    if new_paddle2_pos <= HALF_PAD_HEIGHT:        paddle2_pos = HALF_PAD_HEIGHT    elif new_paddle2_pos >= HEIGHT-HALF_PAD_HEIGHT:        paddle2_pos = HEIGHT-HALF_PAD_HEIGHT    else:        paddle2_pos = new_paddle2_pos        # draw paddles    canvas.draw_polygon([[0, paddle1_pos-HALF_PAD_HEIGHT],             [PAD_WIDTH-1, paddle1_pos-HALF_PAD_HEIGHT],            [PAD_WIDTH-1, paddle1_pos+HALF_PAD_HEIGHT],             [0, paddle1_pos+HALF_PAD_HEIGHT]], 1, "White", "White")    canvas.draw_polygon([[WIDTH-PAD_WIDTH+1, paddle2_pos-HALF_PAD_HEIGHT],             [WIDTH, paddle2_pos-HALF_PAD_HEIGHT],            [WIDTH, paddle2_pos+HALF_PAD_HEIGHT],             [WIDTH-PAD_WIDTH+1, paddle2_pos+HALF_PAD_HEIGHT]], 1, "White", "White")    # draw scores    canvas.draw_text(str(score1), (WIDTH/4, HEIGHT/5), 50, "Red")    canvas.draw_text(str(score2), (WIDTH/4*3, HEIGHT/5), 50, "Red")    canvas.draw_text("Author: Tiny656", (WIDTH/5*3, HEIGHT/20*18), 18, "Green")    canvas.draw_text("Mail: 236798656@qq.com", (WIDTH/5*3, HEIGHT/20*19), 18, "Green")    def keydown(key):    global paddle1_vel, paddle2_vel    acc = 3    if key == simplegui.KEY_MAP[w]:        paddle1_vel -= acc    elif key == simplegui.KEY_MAP[s]:        paddle1_vel += acc    if key == simplegui.KEY_MAP[up]:        paddle2_vel -= acc    elif key == simplegui.KEY_MAP[down]:        paddle2_vel += acc        def keyup(key):    global paddle1_vel, paddle2_vel    acc = 3    if key == simplegui.KEY_MAP[w]:        paddle1_vel += acc    elif key == simplegui.KEY_MAP[s]:        paddle1_vel -= acc    if key == simplegui.KEY_MAP[up]:        paddle2_vel += acc    elif key == simplegui.KEY_MAP[down]:        paddle2_vel -= accdef reset():    new_game()        # create frameframe = simplegui.create_frame("Pong", WIDTH, HEIGHT)frame.set_draw_handler(draw)frame.set_keydown_handler(keydown)frame.set_keyup_handler(keyup)frame.add_button("Reset", reset, 200)# start framenew_game()frame.start()

Mini projects ---- Pong